How to create a SPA using SvelteKit with no Node or Server Side Routing

I have a backend REST API written in Python, and I want to make a simple front end in Svelte. The front end should be a Single Page Application and not have to rely on a Node.js server, or any kind of server side routing. I want to serve this over Nginx and rely on client side routing. The front end is "dynamic" in the sense that it needs pages with unknown parameters, for example:

/blog/1
/blog/2

However, it is not quite intuitive as to whether you need to prerender and which build adapter to use. Without the correct settings, the build will most likely fail. Even if you get the build to run successfully, it is hard to test it on your local host.

Add a fallback page to Adapter Static

The key to making this work is to add a fallback page in svelte.config.js; without this you won't be able to build successfully:

import adapter from '@sveltejs/adapter-static';

const config = {
kit: {
    adapter: adapter({
      fallback: 'index.html'
    })
  }
};

Disable Prerendering

Next you need to disable server side routing and prerendering, as well as enable client side routing. In your src/routes/layout.js, add the following:

export const ssr = false;
export const csr = true;
export const prerender = false;

You can actually prerender certain content. But any page with [parameters] must not be prerendered.

Build your SPA

Now you can run npm run build.

In your build directory, you should now see an index.html file:

matthew_moisen_svelte_build_index.jpg

Test the Build

To test it, you would ideally set up an Nginx on docker. However to quickly test it you can use Python's simple server.

On the command line navigate to the build directory and run:

python -m http.server

Now in your browser, go to http://localhost:8000/. Python's web server will default this / to your index.html.

However, if you try to directly load another route, like http://localhost:8000/foo, you will see a 404. To rectify this, run the following Python code from within the build file and try again:

import os
from http.server import SimpleHTTPRequestHandler, HTTPServer


class FallbackHTTPRequestHandler(SimpleHTTPRequestHandler):
    def send_error(self, code, message=None, explain=None):
        if code == 404:
            self.path = "/index.html"
            super().do_GET()
        else:
            super().send_error(code, message, explain)


httpd = HTTPServer(("localhost", 8000), FallbackHTTPRequestHandler)

httpd.serve_forever()

What this will do is always serve the /index.html file as a fallback.


Errors when not using fallback

Without the fallback option in svelte.config.js, you can see errors when you run npm run build such as:

> Using @sveltejs/adapter-static
  @sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic:
    - src\routes/
    - src\routes/bar
    - src\routes/bar/[lol]
    - src\routes/foo

This error is because adapter-static will require all routes to be prerenderable, unless you add a fallback.

Another error you may see if you mark your routes as prerenderable:

Error: The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:
  - /bar/[lol]

In this case, you have a dynamic route, so prerendering won't work.

The solution for SPAs is to use a fallback in svelte.config.js.

Environment variables for SvelteKit SPAs

This is particularly important for local development when you need your SPA connecting to a local webserver.

Add two files to your root directory:

/project
    src/
        ...
    .env
    .env.local

For SPAs, all keys need to be prefixed with PUBLIC_. This will make the variables accessible from +page.svelte files.

For example in .env:

PUBLIC_BASE_URL=

In .env.local:

PUBLIC_BASE_URL=http://localhost:8080

You can then access these from your +page.svelte via:

import { PUBLIC_BASE_URL} from '$env/static/public';

When you build and deploy, having the empty PUBLIC_BASE_URL in .env means that all fetch requests will go to the same host.

Note that if you receive this error it means that your .env files are not located in your root directory:

SyntaxError: ambiguous indirect export: PUBLIC_FOO

jsconfig.json and $lib imports

To get $lib imports working and proper IDE checking in VSCode, add this jsconfig.json to the root directory:

{
    "compilerOptions": {
    "checkJs": true,
    "noImplicitAny": false,
    "paths": {
        "$lib":["./src/lib"],
        "$lib/*":["./src/lib/*"]
    },
},
"exclude": ["node_modules"],
"extends": "./.svelte-kit/tsconfig.json",

}


Comments

Add Comment

Name

Email

Comment

Are you human? + two = 12


Name: Berberoast

Creation Date: 2023-09-18

This has literally saved my life you have no idea how happy I am to have found this page.