-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
True SPA mode #754
Comments
One idea: since SPA mode basically implies 'static', this becomes an const adapter = require('@sveltejs/adapter-static');
module.exports = {
kit: {
adapter: adapter({
fallback: '200.html'
},
prerender: {
enabled: false
},
ssr: false
}
}; It would require a new API to be exposed to the prerenderer, but that's technically straightforward. |
I'm all for this and was thinking about a way that would allow a true SPA mode to still update dynamically on the basis of CMS provided content. If I could dream a perfect SPA mode for SvelteKit it would be static files that could optionally still hydrate with up-to-date content (and also dynamic routes) without having to create a different content-fetching technique outside of edit: |
The thing that makes the client-side invocation return the same data as the server-side one is that the results of calling It sounds like you're asking for the ability to turn that inlining behaviour off, which would certainly be possible. We'd just need to add a new option to export async function load({ fetch }) {
const res = await fetch('/blah.json', { inline: false });
// ...
} That's something that would warrant a separate issue though. |
thanks for clarifying that for me. I was under the impression that with adapter-static |
It is, yeah — and I guess it makes sense to note that the import { browser } from '$app/env';
export async function load({ fetch }) {
const res = await fetch(`/blah.json?inline=${!browser}`);
// ...
} ...since there wouldn't be any inlined data corresponding to |
I think, SPA didn't work after build with adapter-static. This is result after I tried:
<h1>index.svelte</h1>
{JSON.stringify(localStorage)}
<h1>[slug].svelte</h1>
const node = require('@sveltejs/adapter-static');
kit: {
// ...
adapter: node({
fallback: 'index.html',
}),
ssr: false,
// ...
}
{
"routes": [
{ "handle": "filesystem" },
{ "src": "/(.*)", "status": 200, "dest": "/" }
]
} Result for The result are same:
Repo: https://github.com/mzaini30/great-book |
that's because |
Currently in Sapper, there are 2 ways (that I know of) for generating sites:
Unfortunately there is no "middle ground" between these two options as far as I am aware. If an app is created which requires authentication, the Node app option must be used, which is not always desirable.This would be my definition of a "true" SPA mode for SvelteKit:A way to generate a site, which:
For example, in
|
@llui85 I think what you are describing is along the lines of what I wished for in my previous comment. |
I mostly agree with @llui85. I have the same need. I also propose to add pre-built error pages, like |
My problem is same 😀 |
@llui85 that was a very long way of asking for what SvelteKit already provides!
In an SPA there's no such thing as an error page. Every request (that doesn't match a specific prerendered HTML file) is handled by the same fallback page, usually called Some providers will allow you to specify a fallback const adapter = require('@sveltejs/adapter-static');
module.exports = {
kit: {
adapter: adapter({
- fallback: '200.html'
+ fallback: '404.html'
},
prerender: {
enabled: false
},
ssr: false
}
}; |
Not exactly related to the current discussion, but I'm having an issue when trying to build a static SPA (migrating from a "pure" svelte app, based on the rollup template, and Per the troubleshooting documentation (https://kit.svelte.dev/docs#troubleshooting-server-side-rendering), I should migrate all these top-level Unless I'm overlooking something, that will require a rather significant refactoring of all the browser-based library imports, but given I intend to use it for a static SPA, it seems like a hassle to migrate all the imports to dynamic imports just to allow it to run in the server-side prerender. I guess what I'm looking for is a migration path from Svelte-based SPAs, rather than Sapper projects, when including libraries that rely on a global |
The fundamental issue is that the component must be imported in order to determine whether it has an There's a couple of options that spring to mind - we find some other way of establishing whether a given page should be SSR'd, or we determine it with static analysis (which has the usual caveats but should be pretty robust in this case). Having said that, if a module crashes when you merely import it, I would view that as a bug in the module, and raise an issue. |
If the whole app has ssr set to false, it doesn't need to get imported and analyzed though, right? Which would prevent the issue. Or is there still some traversal due to the route manifest? |
That was exactly my thinking as well. I suggested it in #779 (comment) where someone reported the same issue |
@Rich-Harris Thanks for the clarifications. I'm not familiar with Surge, Netlify and other providers like that, I'm used to nginx though. Since SPA would still have a bunch of files to serve this thing would work fine, I would only need to adjust the config to serve @dummdidumm I've tried this and it doesn't really work — or I'm doing something wrong. I tried this with
Should note it never behaved like this in Sapper. The only thing that worked for me was filtering all these deps that have import problems from /**
* Currently we have problems with 3 deps:
* 1. emoji-regex is not ESM compatible.
* 2. two deps of svelte-i18n: fast-memoize, deepmerge. These are not ESM compatible either.
* 3. nanoid. It keeps throwing errors, because SSR has no secure random generator, lol.
*/
noExternal: Object.keys(pkg.dependencies || {}).filter(
name => !['emoji-regex', 'svelte-i18n', 'nanoid'].includes(name),
), Should I open a new issue and provide you with a repro? UPDATE: I saw Rich's response in here, so nevermind. |
@dummdidumm @benmccann individual pages could override the app-level setting |
Ah, thanks for explaining. I'm wondering if it makes much sense as a page-level setting. I'm assuming that we don't ship it in the manifest in order to be able to decide on each navigation whether the page is server or client rendered. If it takes effect only on the first page load of a site, it seems potentially confusing and I can't think of a use case for it. I imagine that being able to disable it across the board will end up being more requested, but maybe my imagination fails me. The other option would be to make the setting more than just a boolean. E.g. we could have |
These more granular options would at least make it possible to skip traversal to see if invidual pages override the app-level setting, which would prevent the "does not work in node environments" library errors. |
This is what I have been looking for for years with Sapper. And it's a game-changing for the Svelte world! Come on guys! YOU ARE REALLY GORGEOUS! |
Hey, just ran into this when i wondered where my index.html is after running npm run build with the static adapter. I didn't quite understand from reading this thread whether it currently is possible to build SPAs with only dynamically routed pages. |
Yes it's possible. I'm using it right now. The index.html is in _app dir. |
Hm, for me it's not. Note that I only have one page which looks like this: |
Naaa. Please install again. Everything works right now with latest versions. |
@frederikhors nope, no html in build anywhere. You can try yourself: just create a new sveltekit app, rename |
I would imagine this issue would be closed if this would already work. |
Sorry I'm still using prerender option... I thought you meant that. |
Trying to make libraries work with Vite is a nonstop pain point for people especially with SSR. E.g. see #905 (comment) and the corresponding Discord thread. It turns out they had to do a dynamic It seems like it will be impossibly hard to get users that want to run SPA apps to go to all the library authors that wrote client-side only libraries and tell them that they need to add server-side support because their web framework is imposing SSR upon them as they try to build a client-side app. Anyway, I thought I'd share this user report because I've seen a lot of stuff like this on Discord. The solution is probably to change the way the setting works in one of the ways mentioned in #754 (comment), but I'm not sure which is most preferable yet. |
Needing to import the templates to resolve the options is also biting a user in #933 (comment) where they're trying to build a site with no-prerendered pages but the build process is trying to connect to the production database because we don't know that there are no prerendered pages until we import the site's modules and templates. We would either need to expose these options in another way or add an option to force disablement of prerendering along with adding an option to force disabling of SPA mode. Hopefully there aren't too many of these. It seems like something that could trip up users by default |
Yes, |
I know maintainers hate comments like, "I have this problem too", but I couldn't resist explaining why this is a very important use case for us. We build self-contained apps using Go and when releasing versions of these apps we embed the files to be served from the binary's embedded file system. Lacking a true SPA mode is what's keeping us from exploring SvelteKit for these sort of deployment scenarios. When we embed the frontend app files, the client has to handle all routing because there is no SSR component. As an alternative to having an SPA mode we've experimented with SSR using things like v8 bindings in Go but have found that the overhead isn't worth the gain for the purpose of the application, especially since SEO isn't a concern. |
@plunkettscott Exactly this! I stumbled upon this issue when i realized i couldn't embed a bundled sveltekit SPA into my crystal binary. |
* rename ssr to respond, since ssr is sometimes false * add tests to adapter-static * add failing test for #754 * implement fallback rendering * render fallback page * update lockfile * changeset * add readme for adapter-static * formatting * gah * missing full stop * remove ESM export for now, no benefit to it * windows * lockfile shenanigans * argh windows * ugh WHAT NOW windows * try this, you dumb timewaster
Implemented — docs here: https://github.com/sveltejs/kit/tree/master/packages/adapter-static |
Thank you! This is was very needed indeed! |
@repomaa have you found a way to fix this? I'm facing the same issue with my config like below: import preprocess from 'svelte-preprocess';
import adapter from '@sveltejs/adapter-static';
import { dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte',
adapter: adapter({
// default options are shown
// pages: 'build',
// assets: 'build',
fallback: 'index.html'
}),
prerender: {
enabled: false
},
ssr: false,
vite: {
// root: "./src",
optimizeDeps: {
include: ['clipboard-copy']
},
resolve: {
alias: {
// $components: resolve(__dirname, "./src/components"),
// $stores: resolve(__dirname, "./src/stores"),
$mixlib: resolve(__dirname, "./src/mix.lib.ts"),
},
},
// esbuild: {
// include: /\.(tsx?|jsx?)$/,
// exclude: [],
// loader: 'tsx'
// }
}
}
};
export default config; |
@Smilefounder what is the problem. True SPA is working today. |
This is something I always wanted, Why have both Svelte and SvelteKit? Why not have just Svelte which can be both at the same time, just Svelte like it is barebones and if you want it to be an App Framework just use the App Framework features, this would make Svelte even more easier to use and userstand because users can go deeper and deeper as they need to. It is the same idea with the library mode which I like you have one framework which can be an app or an component library just apply the same to all around building apps with Svelte. |
@ivanjeremic if you use SvelteKit and adapter-static you can have that. And it's wonderful! |
I do need supply a client-side route though? I hope sveltekit can have an option for SPA-mode at |
@laoshaw the client side router is already included in svelte-kit. You can look at my sample in https://github.com/mattiash/svelte-kit-admin-ui It can be built and served by a static http server and access an http api to fetch data. |
@mattiash Thanks. It seems you removed a few files from src/route, yes I need do this too, was asking sveltekit to provide an option to have this SPA-mode out-of-box. I'm glad to know client-side-router is built-in, still new to svelte, just came over from vue. |
I'm trying to deploy a SvelteKit app for capacitor, since I understood this would work under the hood. Following the current config I get errors such as
Has this been removed, if so, that would really suck, since we want one code base for all platforms 🙈 |
My bad was using the wrong adapter, sorry
|
We now have the
ssr: false
option which gives us something close to SPA mode: https://kit.svelte.dev/docs#ssr-and-javascriptIt doesn't get us all the way there though, because in a typical SPA you would likely want to generate a single fallback page that handles all requests — for example Surge lets you add a 200.html file, while Netlify allows you to add something like this to
_redirects
:By contrast, SvelteKit expects to generate (whether at runtime or prerender time) an HTML page that includes no content, but does include information about the route that should be hydrated, since the router isn't invoked on load. To create a true SPA, SvelteKit needs to create a content-less file that doesn't contain any route information, and the router needs to figure out what JS to load and execute.
I'm not certain how best to do this in a provider-agnostic way.
The text was updated successfully, but these errors were encountered: