How I built a SSR ready Star Wars app with Svelte in just few hours.
Svelte - As everyone know it's a "new" cool kid in the block. If you are still living under the rock, I recommend you to watch this presentation by Rich Harris - the creator of Svelte.
Sapper? Sapper is a Next.js
equivalent for Svelte. It creates a bare-minimum boilerplate with routing, code-splitting, service worker and what not.
Recently I decided to try my hands on Svelte to create an app that supports both server side and client side rendering with those fancy shimmer effects.
So, let's get started.
Implementing SSR in Sapper is fairly simple and straight-forward. All you need is this code-block in your svelte files.
Adding context="module"
in a script tag with an exported preload function implements SSR and CSR with pre-fetch(optional) like waving a magic wand. But there's a problem.
When I click or hover(with rel=prefetch
) on a menu item, Svelte tries to fetch the route chunk and resolves the api calls and only then navigates to the next page. It gives an impression that the app is either frozen or lagging which can be a frustrating user experience.
Clone the repo down below and checkout to the problematic commit hash(4bb9d18
) to experience the issue.
crup / svelte-ssr-swapi
Demo project to integrate SSR with Svelte with workaround for preload lag. https://keen-clarke-08ba4f.netlify.com/
$ git clone https://github.com/crup/svelte-ssr-swapi.git
$ cd svelte-ssr-swapi
$ git checkout 4bb9d18
$ yarn && yarn dev
How to fix this? With only a couple of hours of experience in Svelte, I don't know the right way but this is how I solved it me.
Create a Svelte store and define a key which will be an identifier for for distinguishing if the page is SSR or CSR.
In your routes file, instead of resolving the promise and returning the resolved response in cards
key, read the SSR state and and return promise if isSSR
is false.
Here's the boilerplate:
On your page level component set isSSR
to false
and resolve cards
if it's a promise. Setting isSSR
to false
on first client-side render will enforce script tag with context="module"
to return a promise instead of response.
With this approach we can now resolve apis on client side, show loaders/shimmers without any lag.
Now comes the worst part - rendering this list items. Since we now have an array(server-side) and a promise(client-side). I had to write handlers for both array and promise.
The final version is in master
branch and it looks like this:
Demo
Disclaimer:
This is my very first attempt at Svelte and chances are that I may have done massive blunders like improperly accessing/updating/unsubscribing store, duplicating code in rendering cards and routes. There's a also a room for refactor in final branch. So, use this code at your own risk.
If there's a better way to achieve the same, feel free to create a PR and suggestions are welcome. :)
Top comments (3)
It looks currently broken. The FetchEvent for "swapi.co/api/people" resulted in a network error response: the promise was rejected because it has been blocked by CORS policy.
Access to fetch at 'swapi.co/api/people' from origin 'keen-clarke-08ba4f.netlify.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Hi, thanks for reporting the issue. Apparently, it looks like
swapi.co
is out of service and there's a mirror onswapi.dev
. I've made the required changes and it should be working now.I solved this by only fetching in the preload when isSSR is true. In the mounted hook, I do the reverse and fetch if it's CSR. In both cases the object type is an array. But it's only populated in preload on the server