In this article I will show you the fundamental steps to use SvelteKit as a Static Site Generator and elaborate on some related topics afterwards.
The case for static sites
Getting into SvelteKit with little to no experience in Node.js is probably easiest by building a fully static site and removing the need for a Node server completely. A static site will run fairly well on the cheapest of hosting solutions and especially a SvelteKit static site will obliterate any common CMS in terms of performance when run on the same server.
Other benefits of a static site are security, easy version control, portability and arguably also flexibility, compared to most CMS.
And a SveteKit static site is not just like any collection of html files you link together, like we used to do back in the days. SvelteKit static sites come with client side navigation and data preloading to make any site truly fast. This blog is a SvelteKit static site, btw.
Creating a new SvelteKit site
Terminalnpm create svelte@latest my-static-site
Pick ‘Skeleton project’ to get a clean install. The installer will ask you a few questions. I’ll pick JavaScript as I am not yet comfortable with TypeScript. You can decide if you want ESLint, Prettier and so on. None of the options are needed for our small static site.
Terminalcd my-static-sitenpm installnpm run dev -- --open
Your SvelteKit site should open in the browser and while you keep the terminal open it will nearly instantly reflect all changes you make to the code.
SvelteKit routing basics
We’ll have to add a few subpages, what would be the point otherwise?
SvelteKit uses a filesystem-based router, which means that the folders and files within your /src/routes
folder directly represent the routes of your project. We’re not going to go in too much detail on the subject as routing is one of the core concepts of SvelteKit, which means you should really study the docs on it.
Just to have something to work with, we are going to create a few routes, add some content and link them all up in a layout file.
Your pages are files which have to be called +page.svelte
. So creating something like this:
📂 src┣ 📂 routes┃ ┣ 📂 one┃ ┃ ┗ 📄 +page.svelte┃ ┣ 📂 three┃ ┃ ┗ 📄 +page.svelte┃ ┣ 📂 two┃ ┃ ┗ 📄 +page.svelte┃ ┗ 📄 +page.svelte┗ 📄 app.html
will give you these routes:
//one/two/three
We’ll just add a simple navigation to all pages via a layout file, so we actually can test out the client side navigation.
/+layout.svelte1<nav>2 <a href="/">Home</a>3 <a href="/one">One</a>4 <a href="/two">Two</a>5 <a href="/three">Three</a>6</nav>7 8<slot />
We’ll just need to add anything to the pages so we actually see something happening when we navigate:
/src/routes/one/+page.svelte1<h1>One</h1>2 3<p>Some example content</p>
All the settings needed to make a site fully static
For a completely static site we use the static adapter. So we install it as a dev dependency like so:
Terminalnpm install -D @sveltejs/adapter-static
Then we replace adapter-auto
by the newly added adapter-static
.
/svelte.config.js -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-static'; 3 4/** @type {import('@sveltejs/kit').Config} */ 5const config = { 6 kit: { 7 adapter: adapter() 8 } 9};10 11export default config;
And all that’s left now is to tell SvelteKit that all pages should be prerendered. The most convenient way to do this is in a root layout file.
/src/routes/+layout.js1export const prerender = true;
There’s one more setting you might need to change depending on your hosting. By default SvelteKit builds routes/about
to about.html
. That means, when receiving a request to your-domain.tld/about
, your server needs to serve the about.html
file.
If it is not set up to do that it might be changed, for example on an Apache server by means of an .htaccess file with rewrite rules. Or you can add the following setting:
/src/routes/+layout.js1export const prerender = true;+export const trailingSlash = 'always';
The trailing slash setting on ‘always’ does change how your static site is built. routes/about
will no longer lead to a about.html
but instead to an index.html
in an /about
folder.
So if in doubt just add this setting. It will make your build folder slightly more complex but it will work anywhere out of the box.
Build and preview your static site
Now just run npm run build
and Vite will build your site inside the build
folder. The ‘official’ way to test out that site is to run npm run preview
.
I prefer npx serve build
as it sometimes gets better results (meaning closer to how it will really behave on the server).
That’s already it. You can just grab the content of the build folder and upload it to any cheap shared hosting or whatever you have available.
Related topics
Now you already know the basics to get a simple static site built with SvelteKit. Read on if you want to learn a few more related concepts. I plan to add more in the future and would love to hear your ideas on what could be useful.
Adding sleek page transitions
Adding some nice page transitions, for the whole page or only the content area, can be an easy upgrade for your site. Check out my blog post on smooth page transitions if you want to look into that.
Adding meta data
We will store information that’s supposed to end up in the <head>
tag like the page title or meta description in a +page.js
file that is stored alongside the +page.svelte
file.
/src/routes/one/+page.js1export async function load() {2 return {3 title: "Page One"4 }5}
And this is how we retrieve and use the meta data in our root layout:
/src/routes/+layout.svelte 1<script> 2 import { page } from '$app/stores'; 3 4 export let data; 5</script> 6 7<svelte:head> 8 <title>{$page.data.title ?? "My Fallback Title"}</title> 9</svelte:head>10 11<nav>12 <a href="/">Home</a>13 <a href="/one">One</a>14 <a href="/two">Two</a>15 <a href="/three">Three</a>16</nav>17 18<slot />
I’m not going to go in detail on how all this works but if you are curious you can look up these concepts in the docs: Page data and $app/stores.
Keeping routes private
I have yet to find another way to protect routes from being included in a build that is as straightforward as to just move them out of the routes folder before building the project. If you know a simple way, let me know.
Be aware that even if you set export const prerender = false
for certain routes or route groups and don’t link to them, their content will still be included in the build. It won’t be possible to load those URLs directly but on-page navigation would still load the content.
What you should know about preloading
This is what comes out of the box with any SvelteKit app:
/src/app.html 1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="utf-8" /> 5 <meta name="viewport" content="width=device-width" /> 6 %sveltekit.head% 7 </head> 8 <body data-sveltekit-preload-data="hover"> 9 <div style="display: contents">%sveltekit.body%</div>10 </body>11</html>
This attribute on the body tag sets the default behavior for all the on-page links of your site and this particular setting means that users only need to hover over an internal link for all the data of the linked page to get loaded.
That’s what we want to happen most of the time but there might be cases, like with very data-heavy pages on high-traffic sites where we don’t want to transfer a ton of data that might not even be requested in the end.
Check out the official docs on link options to see all possible options.