Svelte/Sapper.js - How to initialize store with localStorage data?
Asked Answered
H

1

20

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.sveltefile 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.

Hotel answered 17/6, 2019 at 18:14 Comment(0)
S
21

At present, SSR is non-optional. There's an issue open for an SPA mode — https://github.com/sveltejs/sapper/issues/383 — that would behave as you describe, we just need to get round to it.

We also plan to have built-in support for i18n in a future release: https://github.com/sveltejs/sapper/issues/576

In the meantime, you can fake it by wrapping all your markup in {#if process.browser} — anything inside won't be server rendered, but will be present as soon as JavaScript kicks in.

Semiannual answered 18/6, 2019 at 0:32 Comment(4)
Thanks for the reply! I will definitely be keeping an eye on those issues and will try wrapping the markup in the if block as you said. And also thanks for the all around awesomeness that is Svelte. I know that the project will only get better and better as time goes onHotel
My token is stored in localStorage, how do I get around that with sapper?Ovariectomy
It's worth noting that checking process.browser also appears to work in regular JS code, not just in templates. Thanks!Isaak
process.browser works in my build environment. but it doesn't work when i export.Myoglobin

© 2022 - 2024 — McMap. All rights reserved.