SvelteKit illustration

Create an SPA with SvelteKit including Docker

By: Luc Apr 12 2024 5 minutes read.
The author is available for employment.

Should you or your company interested, please check out my resume

for further contact information.

Remote positions only.

🧑‍💻 This article assumes you have intermediate experience with the command line.

Recently, I been working on a side-project, and I decided to build a single page application for it’s UI. So I wanted to share this information with y’all.

Previously, you would use something like svelte-navigator or svelte-spa-router, In fact, you can still use this tools today!

However, Sveltekit exists for a reason, and that is to have an opinionated way for building web applications with Svelte.

As Rich Harris said, the Svelte framework creator, other solutions might have their flaws.

I won’t list the reasons why server-side rendering is better or when should you build an SPA instead. Every choice has it’s pros and cons.

Getting started

Let’s create a new project

npm create svelte@latest my-app
pnpm create svelte@latest my-app
npm create svelte@latest my-app

The program will make you some questions, I will choosing the option that says “SvelteKit demo app”, and I will choose to use TypeScript.

I will skip any of the aditional options.

┌  Welcome to SvelteKit!

◇  Which Svelte app template?
│  SvelteKit demo app

◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax

◇  Select additional options (use arrow keys/space bar)
│  none

└  Your project is ready!

Open your favorite code editor with that folder. I will be using VS Code.

Don’t forget to install the packages.

npm i
pnpm i
npm i

How to enable SPA mode

You can also consult the official documentation in this link

Lets create the following file src/routes/+layout.ts

touch src/routes/+layout.ts

Add the following contents:

export const ssr = false;

⚠ Do not confuse this file with +layout.svelte they’re different files, and only in the .ts this will work.

Now we have to switch the adapter, to use the static one.

npm remove @sveltejs/adapter-auto
npm i -D @sveltejs/adapter-static
pnpm remove @sveltejs/adapter-auto
pnpm add -D @sveltejs/adapter-static
npm remove @sveltejs/adapter-auto
npm i -D @sveltejs/adapter-static

Open the file that contains the svelte config svelte.config.js

Replace the adapter to use the static one, here is how it should look like:

import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// Consult https://kit.svelte.dev/docs/integrations#preprocessors
	// for more information about preprocessors
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter({
			fallback: 'index.html' // may differ from host to host
		})
	}
};

export default config;

This should be enough to turn it into an SPA.

You can run the application if you want to play around with it.

npm run dev
pnpm run dev
npm run dev

How to dockerize any Single-page-application

TL;DR: Run any http server that allows rewrites and redirect everything to “index.html”.

Let’s do that! For that, I will using nginx.

✍ Create a file called nginx.conf

events {}

http {
    default_type application/octet-stream;
    include /etc/nginx/mime.types;

    server {
        listen 3000;
        root /usr/share/nginx/html;

        
        gzip on;
        gzip_types text/html application/javascript application/json text/css;

        location / {
            try_files $uri $uri/ $uri.html /index.html;
        }

        location ~* \.(?:css|js|svg|jpg|png|webp|mp4|m4a|webm|ogg|opus)$ {
            expires 30d;
            add_header Cache-Control "public";
        }

        location ~* \.(?:json)$ {
            expires 1d;
            add_header Cache-Control "public";
        }
    }
}

You can tune in this config if you want to, here you can see that it gives a long time cache for any static request.

Create a file called .dockerignore

touch .dockerignore

Add the following contents:

.git
.gitignore
dist
build
Dockerfile*
node_modules
*.md
.env*
.svelte*

Create a file called Dockerfile

touch Dockerfile

As expected, the contents vary depending on the package manager you are using.

FROM node:lts AS builder
WORKDIR /app

COPY . .

RUN npm ci
RUN npm run build

FROM nginx:latest as runner

COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
FROM node:lts AS builder
WORKDIR /app
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@latest-8 --activate

COPY . .

RUN pnpm install
RUN pnpm run build

FROM nginx:latest as runner

COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
FROM node:lts AS builder
WORKDIR /app

COPY . .

RUN npm ci
RUN npm run build

FROM nginx:latest as runner

COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf

Let’s build the docker image:

docker build -t my-app .

Run the container with the following command:

docker run -p 3000:3000 -t my-app

This will forward the port “3000” from the container to match :3000 on your local computer.

🧠 Learn more about docker run

That should be all!

👋 Happy coding!

Tags:
  • webdev
  • typescript
  • svelte
  • docker
Share on Mastodon
Share on X (Twitter)