I come from a React background, but I'm switching to Svelte and Sapper for my next application in order to fight the massive bundle size that comes with React these days. However, I'm having trouble initializing Svelte's store with data retrieved from localStorage.
As per the Sapper docs (https://sapper.svelte.dev/docs#Getting_started), I created my project by running npx degit "sveltejs/sapper-template#rollup" my-app
from the command line. I then installed the dependencies and removed the demo code in the src
folder.
I then created two files: src/routes/index.svelte
and src/store/index.js
.
Code for both:
src/store/index.js
import {writable} from "svelte/store";
export let userLang;
if(typeof window !== "undefined") {
userLang = writable(localStorage.getItem("lang") || "en");
} else {
userLang = writable(null);
}
src/routes/index.svelte
<script>
import {userLang} from "../store";
</script>
<p>Your Preferred Language: {$userLang}</p>
When I run the application and hit the index
route, I see this:
Your Preferred Language: null
which then almost immediately updates and changes to
Your Preferred Language: en
when there is no lang
item in localStorage, and changes to
Your Preferred Language: fr
After explicitly setting localStorage.setItem("lang", "fr")
from the developer console and refreshing.
I know that the store is being initialized on the server first where window
is undefined
and then is being rehydrated on the client. So this behavior is expected.
So my question is: how can I skip the server initialization entirely? Is it possible to only set up the store on the client (where localStorage
is defined) so that the user's chosen language is immediately available?
I can't default to having everything in English or any other language after the user has chosen to change their preferred language. I also can't get the user language from the browser via navigator.language
on initial page load either since navigator
is undefined
on the server as well.
And having a flash of empty text appear before the store rehydrates would screw up the UX for my application, especially when the value of userLang
is going to be used all over the place with translations.
So any strategies or hacks for this are definitely appreciated.
**** Deeper Issue ****
I would actually prefer to not have server-side rendering at all for this application, but I do need all the other excellent features that Sapper provides, like routing, prefetching, and static site building.
So I tried running npx sapper export
as per the docs to generate a completely static site in an effort to remove the server from the equation, but the exact same issue still occurs, even though there is no server being used at all.
Does anyone have any advice on how to configure Sapper and turn off SSR but keep the other features?
Thank you!
**** Update ****
As per Rich Harris's answer, wrapping the markup with {#if process.browser}
does the trick just fine. So I've updated the src/routes/index.svelte
file like so:
<script>
import {userLang} from "../store";
</script>
{#if process.browser}
<p>Your Preferred Language: {$userLang}</p>
{/if}
And the userLang
variable is immediately set with the value from localStorage
or defaults to en
as I intended for this simple demo. There is no more flash of null
, so it's essentially behaving like it's client-side only at this point.
I will work on fleshing out my project and see if there are any more issues I encounter. Til then, I think this solves my issue.