SvelteKit Is Interesting

Maybe that isn’t quite right. SvelteKit with adapter-static is interesting. Basically, this blog is written in Markdown and consumed via SSR (server-side rendering) to produce output index.html files which can be statically served.

I love it. However, I came across some wording in the docs that made me think twice about using SvelteKit with adapter-static for anything other than truly static stuff (like this blog).

In other words, any app that involves user sessions or authentication is not a candidate for adapter-static, even if individual pages within an app are suitable for prerendering.

Reference

That is a strange thing to say… I wonder why that is. So, I tried out a simple experiment wherein I tried to set a simple variable to localStorage to mock authentication (basically a true or false to determine whether I am logged in). Imagine my surprise when the blog failed to build because:

ReferenceError: localStorage is not defined

AHHHHH, right… this is a NodeJS app that is running continuously and building my static html, and it does not have localStorage in the traditional sense. Put another way, this portion of the Svelte code is not run on the client’s machine in JS, but rather run in NodeJS whose output is used as the static site. Okay, but it turns out that I can still do Svelte-stuff on the client-side, but I have to denote it as such. Essentially, I can use onMount to say “this is code to run on the client and not hydrate in NodeJS”.

Let’s look at an example. This page, for instance, has the following:

<script>
    import {onMount} from "svelte";

    let logged_in = false;

    onMount(() => {
        if(!JSON.parse(localStorage.getItem("visited"))) {
            localStorage.setItem("visited", "true");
            localStorage.setItem("logged_in", "false");
        }
        logged_in = JSON.parse(localStorage.getItem("logged_in"));
    });

    function toggle() {
        logged_in = !logged_in;
        localStorage.setItem("logged_in", JSON.stringify(logged_in));
    }
</script>

{#if logged_in}
    <button on:click={toggle}>Logout</button>
{:else}
    <button on:click={toggle}>Login</button>
{/if}

### Logged In: {logged_in}

Logged In: false

You can click that button, and it will store the state. Subsequent reloads will maintain that state as well, so this effectively gets me what I want though the docs claim adapter-static is not a good candidate… confusing. Well, one reason I can see this being an issue is that while we do persist this logged_in state to localStorage (mocking what we would do with a session cookie), subsequent reloads still load the page with the initial state which causes Svelte to edit the page. This happens very quickly, but it’s still a bit jarring to see - try refreshing and watching the “Logged In” header. The default value is false and if you have set it to true, then the page will update onMount.

One simple thing to try would be to replace the default content of the component with a spinner, but that only really works for pages (I would not want every little component being a spinner until the page loaded - that would be wild). Basically, I would create a writeable-store that would allow call components in the render tree to “register” themselves along with a status like “still rendering” or “not yet ready”. Then, in the most sensible place in the view, render the subcomponents inside of a block with a conditionally set classname that determines whether they are hidden. The top-level component would have to poll (or spinlock, more likely) until all the subcomponents are ready, but it would then switch the classname, thus removing the spinner and replacing with the actual content.

Very complicated… I tried to do it here, and it ended up being way more complicated than I thought it would be. Essentially, that registration step would require some non-trivial work to implement. It requires that each component essentially have its own unique identifier so it can be looked up later, and then it also requires that each component register when it is ready to render. None of this is impossible, but definitely more than the scope of this little post.

Anyway, I still really like SvelteKit and have found some workarounds for its shortcomings, but it might require a bit of tooling before I would consider to build a fully-fledged client application in it.

P.S.

This blog post is actually really interesting because of the technologies involved. You should check out the source code to get a really good grasp of what I mean. It is Svelte, it is Markdown, and it is all cobbled together in a way that “just works” and I was amazed.