Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

SPA mode #383

Closed
Rich-Harris opened this issue Aug 28, 2018 · 39 comments
Closed

SPA mode #383

Rich-Harris opened this issue Aug 28, 2018 · 39 comments
Labels

Comments

@Rich-Harris
Copy link
Member

A few people have asked if Sapper has a mode where it doesn't server-render components, but instead just handles the routing and serves a blank page for the client-side app to fill in.

I don't think it makes sense in many situations, but it could make sense in some. We could have something like this:

.use(sapper({
  manifest,
  ssr: false
}))

It would also be useful perhaps to have a hybrid mode that SSRs as much as it can but also creates a blank 200.html page (or whatever) to cover pages that can't be rendered ahead of time (some services like Surge allow you to use 200.html to mean 'any route that doesn't have its own page already).

@kylecordes
Copy link

kylecordes commented Aug 28, 2018

Most ideally Sapper build output would include two different "versions" of the server:

  • The fully SSR server as exists now
  • a simplified server that only includes the non-SSR-render endpoints, in conjunction with a set of statically rendered files which can be served via CDN or any other non-Node, non-SSR web server machinery.

Obviously the SSR is optimal for minimizing client-side first render time and many other things. But there are plenty of use cases where the SSR matters little, and the convenience of static serving matters a lot. Producing both kinds of output (especially for a test suite with a nontrivial app) would also guide Sapper development down a path of useful rigor, I think.

@akaufmann
Copy link

In my eyes, Sapper is a perfect SSR framework and should focus on exactly that - do one thing and do it well. Each additional deviation costs time and makes the code more complex and error-prone.

I understand why this feature is wanted and that it could make one or the other case easier.
I have used many SPA/SSR frameworks and never thought why this SSR framework is not also an SPA framework. Just use the right tool for the job. There are plenty out there and if not why not implement a separate (community) SPA framework with Svelte/Router/Polka that does this job perfectly?

@Rich-Harris
Copy link
Member Author

@akaufmann I definitely have some sympathy for that view. The counter-argument is that creating such an alternative would be a lot of work, especially if it were to have feature parity with Sapper, whereas adding this feature to Sapper would require almost none — it essentially consists of changing this line:

-.replace('%sapper.html%', () => html)
+.replace('%sapper.html%', () => ssr ? html : '')

(You'd also want to avoid running the preload functions, and apps would want to set hydrate: false in the client webpack config as a small optimisation, but that's basically it.)

@antonheryanto
Copy link

other scenario is that we are not running node as backend and just require spa mode (simple apps) but still develop using sapper, but the export result (which is not static but load backend data) are only rendered index.html and client js

@akaufmann
Copy link

@Rich-Harris That's really not much to adjust 😃 I already thought that this feature is at the expense of users who use Sapper as an SSR framework.

@kylecordes
Copy link

Here is the use case I was thinking of:

  • Begin development with Sapper, such that the build output is just static files to be deployed - to a company's existing serving infrastructure. (XHRing to an existing API backend.)
  • Project grows and become successful... in an environment where it might not have gotten off the ground, if the very first deployment required deploying a Node server.
  • The need for SSR rises.
  • The entrenched application is now important enough to warrant server-code deployment.
  • ... and Sapper built output already supports this with no code change.

@timhall
Copy link

timhall commented Sep 1, 2018

Just for reference, I was able to get this to work on one of my projects with: https://github.com/timhall/sapper-spa

It basically returns an empty html for components/routes and clears out preloaded data so that the preload function runs client-side. It’s working great for building an Electron app (I attempted to run the sapper dev server from the context of Electron, but there were a few too many issues).

I like the ssr: false option, it matches well with svelte’s option and thanks for the note on hydrate, I need to make sure to turn that off.

@lillem4n
Copy link

When adding this, I'd love a hydrate-option then as well. Especially when developing the 500-errors on server-side just swishes by sometimes and the client hydration cleans it up. Now it's easier to disable javascript in the browser and work from there, but I like options. :)

@hepiBatman
Copy link

Wasm Emscripten does not work for SSR. As web-assembly use is more prominent, support for non-SSR will be more in demand.

@tomByrer
Copy link

tomByrer commented Aug 17, 2019

Wasm Emscripten does not work for SSR.

WASM sure can work in SSR:
Cloudflare Workers
Rust's Percy
& I think there is some GoLang WASM stuff that can SSR.

Irregardless, 'hybrid SSR-SPA mode' would be very useful. Since Sappier lacks this mode, it makes Gatsby look more attractive for some projects to me. It mixes SSR & in-browser hydration well.

@hepiBatman
Copy link

WASM probably can but not with Emscripten. I can only get it working with sapper by running it on client as Worker.

#849

@jakobrosenberg
Copy link

jakobrosenberg commented Sep 21, 2019

SSR is about SEO and user experience. But what about developer experience? That may be why most of us are here. I assume.

Forcing SSR (in some cases)

  1. increases build-time while developing
  2. adds more moving parts
  3. obfuscates debugging and troubleshooting
  4. complicates authentication
  5. complicates offline-first apps
  6. complicates security (with SPA only endpoints have to be secure)
  7. delays page load according to the slowest endpoint response

While odds are that I'm wrong about some of the claims, there are a lot of reasons to not force SSR. Especially in developer mode.

When I run npm run build I build a bundle that's optimized for the end user. When I run npm run dev I run a process that's optimized for development. Forcing SSR is like forcing minifiers, obfuscators and other build tools. Sure, I can check that they work every time I save a file, but do I want to? That's not how testing is done anyway.

Currently my server build-times increase gradually to 10+ seconds until I restart Sapper with npm run dev. Without forced SSR (at least in dev mode) the issue would be easier for me to debug. Hell, the issue probably wouldn't even be there to begin with.

@swyxio
Copy link

swyxio commented Oct 7, 2019

i'm on the side of this being out of scope for sapper. there are templates and cli tools that do the SPA thing and only the SPA thing.

also using sapper idiomatically means colocating data fetch routes, which isn't usable anymore in SPA mode. so you start having to write apps differently under SPA mode, instead of being just a CLI flag. this probably is an indication its a problem for a different tool

@jakobrosenberg
Copy link

jakobrosenberg commented Oct 10, 2019

@sw-yx I don't see the relevance of having to write apps differently in SPA mode. Isn't that the whole purpose of a mode versus something like a theme.

Different doesn't necessarily mean difficult. In this case different actually means easier (for all the reasons listed in my previous post).

As an end-developer you can pick whichever mode you prefer. So it boils down to whether it's worth maintaining the feature for the core developers.

As Rich wrote

...an alternative [framework] would be a lot of work, especially if it were to have feature parity with Sapper, whereas adding this feature to Sapper would require almost none — it essentially consists of changing this line:

-.replace('%sapper.html%', () => html)
+.replace('%sapper.html%', () => ssr ? html : '')

Is it worth reinventing the wheel if the solution is that simple?

@khrome83
Copy link

khrome83 commented Oct 10, 2019 via email

@igregson
Copy link

why?

It would also be useful perhaps to have a hybrid mode that SSRs as much as it can but also creates a blank 200.html page (or whatever) to cover pages that can't be rendered ahead of time

+1 for a hybrid mode

One particular use case I have in mind is needing part of a site to be a SPA and the rest of it to be fully static. For example: a site that's 80% static and 20% runtime dynamic (e.g. an authenticated users section) where the 80/20 split cleanly maps to routes.

My understanding is that Gatsby and Gridsome can somewhat do this, but not in a very clean way (they don't have any way to exclude full routes from SSR, that I know of – please correct if wrong!).

@jakobrosenberg
Copy link

The point of Sapper is file system routing

You don't need SSR for file system routing. In fact, this was my initial concern - that you have to buy into all the issues that come with SSR just to use a file system router.

I since wrote svelte-filerouter and ported my projects to Svelte.

For me it makes no difference now whether Sapper adopts SPA feature, but for anyone wanting an official, maintained library, I can see why this is a concern.

@rburnham52
Copy link

I'd be interested in just the client side routing as our backend is .net at the moment. Adding a node backend would be overkill. Perhaps if you could do the SSR at built time?

@nitroboy720
Copy link

+1 Would greatly appreciate if there was a SPA mode!

@akauppi
Copy link

akauppi commented Dec 11, 2019

I came to Sapper in order to make an SPA -- gosh, even the name SAPper is so close come to think of it. The web site mentions SPA (and SEO) so while I don't really benefit from the SEO, I took the plunge.

Happy I found this discussion. Not sure any more, whether I'm using the right tool but at least I know about the concerns and the apparent haziness.

Won't toss my -1/+1 here, since I'm too newbie for Sapper to understand the depth of the design decisions and their impact on the project. I came for the file system routing, mainly. Now, I'll check the mentioned svelte-router and svelte-filerouter instead.

Thanks to everyone who commented! Transparency like this makes open source so nice to work with. Even with an occasional dead end.

p.s. Once the course is clear (hybrid or not), editing the project front page to reflect it may be good.

@sido420
Copy link

sido420 commented Jan 13, 2020

Whats the ETA on this?

Trying to use the same sapper code base for both web app and Cordova app. Is this something already supported? Are there any techniques that I can use to make it work? Is SPA the solution for my use case? My app is dynamic and requires authnetication and user login etc so exporting the app is not going to work.

@rustybuddha
Copy link

Atleast give option to disable SSR for a specific route which can't be rendered ahead of time. I can't have my entire website ditch SSR, just for the one route which can't be SSR (throwing many errors on server-side which otherwise works on client-side)

Solving this problem at route level should be easier, instead of, at component level. (I don't know enough to stand by this, just a speculative opinion)

@evdama
Copy link

evdama commented Jan 18, 2020

I really enjoy using Sapper, but integrating Sapper with Firebase is currently a real headache because of the way Firebase libs aren't build for SSR atm codediodeio/sveltefire#4

So maybe really having the option to disable SSR for particular routes could ease a lot of pain and simplify the developement experience for may developers.

Currently the combo of SSR and firebase getting confused between server side and client side (cjs vs esm builds) is pretty much a showstopper when it comes to integrating Sapper & Firebase properly.

My hope is that long term, maybe, since I'm also running my Sapper SSR function inside a firebase cloud function, which in turn runs on node 10 atm, the upgrade to node 13 with ESM support will improve things too :)

@markus
Copy link

markus commented Mar 2, 2020

It seems to be possible to export a static site from Sapper and to avoid server-side data fetching with conditional preloading like this:

<script context="module">
  export async function preload(page, session) {
    if (typeof window == 'object') {
      // load data from the (legacy/non-js) backend
    } else {
      // assign an empty array or whatever is needed to render an empty page
    }
  }
</script>

And triggering the client side preload on the initial page load seems to be possible by adding

__SAPPER__.preloaded = [];

to the end of src/client.js.

Does this make any sense or is there a better way to export a static site that loads data on the client only? Repeating the typeof window check in every preload is a bit annoying of course.

@Conduitry Conduitry mentioned this issue Apr 3, 2020
@pjebs
Copy link

pjebs commented Apr 4, 2020

@markus is your code snippet for avoiding server-side data fetching OR for ditching node altogether (such as for using a Go backend)?

@pjebs
Copy link

pjebs commented Apr 4, 2020

I'm trying to use a Go backend and have nothing to do with node.js. Has anyone managed to achieve such a feat?

I don't really care much about server-side rendering (but if you can get it to work on Go using a javascript interpreter pkg such as otto or goja, that would be nice)

@pjebs
Copy link

pjebs commented Apr 4, 2020

@Rich-Harris Is there a reason why you don't want to support non-node.js backends? Obviously Svelte is amazing. The typescript issue irritates some who consider it an adoption blocker but not all.

Don't you think making it too opinionated may work against your project? Obviously SSR is a nice-to-have and requires node (to do conveniently), but for many projects it's not a high priority. A higher priority is for those teams (that have a distinct FE and BE teams) to use a different language for their backend which they are more familiar with, and arguably superior to node.js.

@antony
Copy link
Member

antony commented Apr 4, 2020

@pjebs the maintainers team does not have the bandwidth to support alternate backends, especially ones in different languages. It isn't on the Roadmap. Please keep casual discussion to the https://svelte.dev/chat to avoid cluttering up issues.

@vortacio
Copy link

vortacio commented Apr 7, 2020

So I'm currently learning Svelte and think it's awesome. Coming from Vue and Angular I think Svelte is just better in many ways. However, I build a lot of web apps and sometimes I need SSR and sometimes I do not. For the last year I've been using Nuxt and its been great. Thought I was going to give Sapper a try but I guess not yet. Is SPA mode forever going to be neglected in Sapper?

@antony
Copy link
Member

antony commented Apr 9, 2020

@vortacio Since you can build SPAs with Svelte, and there are a huge number of routers out there, adding an SPA mode to Sapper isn't very high on the priority list at the current time.

We'd happily take a PR for an SPA mode, and we're not writing it off, but there are higher priority tasks which are going to take precedence, and no amount of foot stomping is going to change that.

@benmccann
Copy link
Member

A couple cases where this might be helpful:

  • testing (manual or integration)
  • load shedding (if servers are getting overloaded then offload rendering to client devices)

@benmccann
Copy link
Member

benmccann commented Jun 11, 2020

I actually think that this will be much more generally useful than my initial reaction was

If we implement preload headers for all the assets (#408) then Sapper will have a lot of smarts on the server-side even for a client-side rendered app. This is something you couldn't get from a client-side router alone and would be a complex middleware implementation leveraging the bundler. So for people that want a CSR SPA app, Sapper would provide a lot of value over other routers

And I actually think there are actually a lot of reasons a CSR app might be preferable to hydration. There's an article from Google I highly recommend reading on this topic. Some points:

  • time-to-interactive may be worse in a hydrated application
  • hydration requires sending your data to the client twice (once in the rendered HTML and once as a JSON blob to populate the initial application state)
  • SSR is operationally difficult to scale. Node excels at async I/O and has the hardest time with CPU-bound operations. Rendering HTML is CPU-bound, so you're asking Node to do the thing it's worst at. This problem can be solved by running many Node processes (one per CPU core across machines) and properly load balancing between them, but it's not the simplest setup to scale
  • at the current time, Svelte's hydration is a bit lacking. it actually causes the whole dom to rerender on the client. doing this rendering on both the client and server is expensive. while this is something that may be able to be improved it seems much harder than adding this option. (related tickets: React-like efficient hydration svelte#4975 Fast hydration & FIX iframes hydration svelte#4309). hydration implementations in React and elsewhere are only becoming more complex with incremental/progressive/partial hydration

@benmccann
Copy link
Member

I've sent a PR for this. The implementation was pretty close to what @Rich-Harris had suggested (it looks like the code may have changed a little since then): #1275

@gopherine
Copy link

Not sure if this is possible in here, but is it possible to server render on something else other than, NodeJS, from what I understand currently it is not given the sapper. Middleware dependency.. However, i would love to see a pluggable mode.

The reason being, why even use NodeJS to render sapper, when the goal is to have a backend in some other language. Rather it would be much better if it's pluggable to anything programming language, be it node, go or rust and the UI should just be pluggable.

Now it's just an idea I don't know how feasible this is to implement.

@benmccann
Copy link
Member

This will be added in SvelteKit: sveltejs/kit#231. We won't be adding major new features to Sapper

@btakita
Copy link
Contributor

btakita commented Jul 14, 2021

What is the recommended path to somebody wanting to maintain a Sapper app in both PWA & SPA mode? I having issues migrating to Svelte Kit b/c "type": "module" does not support ts files since the js extension is necessary for imports.

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '...' imported from ...

I have a requirement to serve the site as a PWA & as a mobile app using the file:// protocol.

@benmccann
Copy link
Member

SvelteKit supports TypeScript out-of-the-box. I'm not quite sure what issue you're having with SvelteKit, but I'd recommend asking for support in Discord and then filing an issue with a repository that reproduces the issue if no one on Discord knows how to solve it. Sapper is not going to be receiving many if any updates

@btakita
Copy link
Contributor

btakita commented Jul 14, 2021

I'm having an issue using svelte-kit build, "type": "module" & importing ts module from another ts module. The following error occurs.

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '...' imported from ...

I'll ask again on Discord.

@btakita
Copy link
Contributor

btakita commented Jul 18, 2021

@benmccann Are there any recommended alternatives other than svelte-kit to migrate to from Sapper? I'm unfortunately running into some weird issues with the svelte-kit migration & running out of time to do the migration. I need an SPA using hash routing & the file:// protocol. Thanks.


Edit: I'm hoping that https://github.com/ItalyPaleAle/svelte-spa-router or Routify will work to create the SPA app & buy some time for the Vite SSR issues to be resolved.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests