Introduction

Before we begin

SvelteKit is in early development, and some things may change before we hit version 1.0. This document is a work-in-progress. If you get stuck, reach out for help in the Discord chatroom.

See the migration guides for help upgrading from Sapper.

What is SvelteKit?

SvelteKit is a framework for building extremely high-performance web apps. You're looking at one right now! There are two basic concepts:

  • Each page of your app is a Svelte component
  • You create pages by adding files to the src/routes directory of your project. These will be server-rendered so that a user's first visit to your app is as fast as possible, then a client-side app takes over

Building an app with all the modern best practices — code-splitting, offline support, server-rendered views with client-side hydration — is fiendishly complicated. SvelteKit does all the boring stuff for you so that you can get on with the creative part.

You don't need to know Svelte to understand the rest of this guide, but it will help. In short, it's a UI framework that compiles your components to highly optimized vanilla JavaScript. Read the introductory blog post and the tutorial to learn more.

Getting started

The easiest way to start building a SvelteKit app is to run npm init:

npm init svelte@next my-app
cd my-app
npm install
npm run dev

This will scaffold a new project in the my-app directory, install its dependencies, and start a server on localhost:3000. Try editing the files to get a feel for how everything works – you may not need to bother reading the rest of this guide!

Routing

At the heart of SvelteKit is a filesystem-based router. This means that the structure of your application is defined by the structure of your codebase — specifically, the contents of src/routes.

You can change this to a different directory by editing the project config.

There are two types of route — pages and endpoints.

Pages typically generate HTML to display to the user (as well as any CSS and JavaScript needed for the page). By default pages are rendered on both the client and server though this behavior is configurable.

Endpoints run only on the server (or when you build your site, if prerendering). This means it's the place to do things like access databases or APIs that require private credentials or return data that lives on a machine in your production network. Pages can request data from endpoints. Endpoints return JSON by default though may also return data in other formats.

Pages

Pages are Svelte components written in .svelte files (or any file with an extension listed in config.extensions). By default, when a user first visits the application, they will be served a server-rendered version of the page in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel where the common portions in the layout do not need to be rerendered.

The filename determines the route. For example, src/routes/index.svelte is the root of your site:

<!-- src/routes/index.svelte -->
<svelte:head>
	<title>Welcome</title>
</svelte:head>

<h1>Hello and welcome to my site!</h1>

A file called either src/routes/about.svelte or src/routes/about/index.svelte would correspond to the /about route:

<!-- src/routes/about.svelte -->
<svelte:head>
	<title>About</title>
</svelte:head>

<h1>About this site</h1>
<p>TODO...</p>

Dynamic parameters are encoded using [brackets]. For example, a blog post might be defined by src/routes/blog/[slug].svelte. Soon, we'll see how to access that parameter in a load function or the page store.

Endpoints

Endpoints are modules written in .js (or .ts) files that export functions corresponding to HTTP methods. For example our hypothetical blog page, /blog/cool-article, might request data from /blog/cool-article.json, which could be represented by a src/routes/blog/[slug].json.js endpoint:

type Request<Context = any> = {
	host: string;
	method: 'GET';
	headers: Record<string, string>;
	path: string;
	params: Record<string, string | string[]>;
	query: URLSearchParams;
	body: string | Buffer | ReadOnlyFormData;
	context: Context; // see getContext, below
};

type Response = {
	status?: number;
	headers?: Record<string, string>;
	body?: any;
};

type RequestHandler<Context = any> = {
	(request: Request<Context>) => Response | Promise<Response>;
}
import db from '$lib/database';

/**
 * @type {import('@sveltejs/kit').RequestHandler}
 */
export async function get({ params }) {
	// the `slug` parameter is available because this file
	// is called [slug].json.js
	const { slug } = params;

	const article = await db.get(slug);

	if (article) {
return {
	body: {
		article
	}
};
	}
}

Returning nothing is equivalent to an explicit 404 response.

The job of this function is to return a {status, headers, body} object representing the response, where status is an HTTP status code:

  • 2xx — successful response (default is 200)
  • 3xx — redirection (should be accompanied by a location header)
  • 4xx — client error
  • 5xx — server error

For successful responses, SvelteKit will generate 304s automatically

If the returned body is an object, and no content-type header is returned, it will automatically be turned into a JSON response. (Don't worry about $lib, we'll get to that later.)

For endpoints that handle other HTTP methods, like POST, export the corresponding function:

export function post(request) {...}

Since delete is a reserved word in JavaScript, DELETE requests are handled with a del function.

We don't interact with the req/res objects you might be familiar with from Node's http module or frameworks like Express, because they're only available on certain platforms. Instead, SvelteKit translates the returned object into whatever's required by the platform you're deploying your app to.

The body property of the request object exists in the case of POST requests. If you're posting form data, it will be a read-only version of the FormData object.

To set multiple cookies in a single set of response headers, you can return an array:

return {
	headers: {
'set-cookie': [cookie1, cookie2]
	}
};

Private modules

A filename that has a segment with a leading underscore, such as src/routes/foo/_Private.svelte or src/routes/bar/_utils/cool-util.js, is hidden from the router, but can be imported by files that are not.

Advanced

Rest parameters

A route can have multiple dynamic parameters, for example src/routes/[category]/[item].svelte or even src/routes/[category]-[item].svelte. If the number of route segments is unknown, you can use rest syntax — for example you might implement GitHub's file viewer like so...

/[org]/[repo]/tree/[branch]/[...file]

...in which case a request for /sveltejs/kit/tree/master/documentation/docs/01-routing.md would result in the following parameters being available to the page:

{
	org: 'sveltejs',
	repo: 'kit',
	branch: 'master',
	file: 'documentation/docs/01-routing.md'
}

src/routes/a/[...rest]/z.svelte will match /a/z as well as /a/b/z and /a/b/c/z and so on. Make sure you check that the value of the rest parameter is valid.

Fallthrough routes

Finally, if you have multiple routes that match a given path, SvelteKit will try each of them until one responds. For example if you have these routes...

src/routes/[baz].js
src/routes/[baz].svelte
src/routes/[qux].svelte
src/routes/foo-[bar].svelte

...and you navigate to /foo-xyz, then SvelteKit will first try foo-[bar].svelte because it is the best match, then will try [baz].js (which is also a valid match for /foo-xyz, but less specific), then [baz].svelte and [qux].svelte in alphabetical order (endpoints have higher precedence than pages). The first route that responds — a page that returns something from load or has no load function, or an endpoint that returns something — will handle the request.

If no page or endpoint responds to a request, SvelteKit will respond with a generic 404.

Layouts

So far, we've treated pages as entirely standalone components — upon navigation, the existing component will be destroyed, and a new one will take its place.

But in many apps, there are elements that should be visible on every page, such as top-level navigation or a footer. Instead of repeating them in every page, we can use layout components.

To create a layout component that applies to every page, make a file called src/routes/$layout.svelte. The default layout component (the one that SvelteKit uses if you don't bring your own) looks like this...

<slot></slot>

...but we can add whatever markup, styles and behaviour we want. For example, let's add a nav bar:

<!-- src/routes/$layout.svelte -->
<nav>
	<a href=".">Home</a>
	<a href="about">About</a>
	<a href="settings">Settings</a>
</nav>

<slot></slot>

If we create pages for /, /about and /settings...

<!-- src/routes/index.svelte -->
<h1>Home</h1>
<!-- src/routes/about.svelte -->
<h1>About</h1>
<!-- src/routes/settings.svelte -->
<h1>Settings</h1>

...the nav will always be visible, and clicking between the three pages will only result in the <h1> being replaced.

Nested layouts

Suppose we don't just have a single /settings page, but instead have nested pages like /settings/profile and /settings/notifications with a shared submenu (for a real-life example, see github.com/settings).

We can create a layout that only applies to pages below /settings (while inheriting the root layout with the top-level nav):

<!-- src/routes/settings/$layout.svelte -->
<h1>Settings</h1>

<div class="submenu">
	<a href="/settings/profile">Profile</a>
	<a href="/settings/notifications">Notifications</a>
</div>

<slot></slot>

Error pages

If a page fails to load (see Loading), SvelteKit will render an error page. You can customise this page by creating $error.svelte components alongside your layout and page components.

For example, if src/routes/settings/notifications/index.svelte failed to load, SvelteKit would render src/routes/settings/notifications/$error.svelte in the same layout, if it existed. If not, it would render src/routes/settings/$error.svelte in the parent layout, or src/routes/$error.svelte in the root layout.

SvelteKit provides a default error page in case you don't supply src/routes/$error.svelte, but it's recommend that you bring your own.

If an error component has a load function, it will be called with error and status properties:

<script context="module">
	export function load({ error, status }) {
return {
	props: {
		title: `${status}: ${error.message}`
	}
};
	}
</script>

<script>
	export let title;
</script>

<h1>{title}</h1>

Server-side stack traces will be removed from error in production, to avoid exposing privileged information to users.

Loading

A component that defines a page can export a load function that runs before the component is created. This function runs both during server-side rendering and in the client, and allows you to get data for a page without (for example) showing a loading spinner and fetching data in onMount.

Our example blog page might contain a load function like the following. Note the context="module" — this is necessary because load runs before the component is rendered:

type LoadInput = {
	page: {
host: string;
path: string;
params: Record<string, string | string[]>;
query: URLSearchParams;
	};
	fetch: (info: RequestInfo, init?: RequestInit) => Promise<Response>;
	session: any;
	context: Record<string, any>;
};

type LoadOutput = {
	status?: number;
	error?: Error;
	redirect?: string;
	props?: Record<string, any>;
	context?: Record<string, any>;
	maxage?: number;
};
<script context="module">
	/**
	 * @type {import('@sveltejs/kit').Load}
	 */
	export async function load({ page, fetch, session, context }) {
const url = `/blog/${page.params.slug}.json`;
const res = await fetch(url);

if (res.ok) {
	return {
		props: {
			article: await res.json()
		}
	};
}

return {
	status: res.status,
	error: new Error(`Could not load ${url}`)
};
	}
</script>

load is similar to getStaticProps or getServerSideProps in Next.js, except that it runs on both the server and the client.

If load returns nothing, SvelteKit will fall through to other routes until something responds, or will respond with a generic 404.

load only applies to components that define pages, not the components that they import.

Input

The load function receives an object containing four fields — page, fetch, session and context.

page

page is a { host, path, params, query } object where host is the URL's host, path is its pathname, params is derived from path and the route filename, and query is an instance of URLSearchParams.

So if the example above was src/routes/blog/[slug].svelte and the URL was /blog/some-post?foo=bar&baz&bizz=a&bizz=b, the following would be true:

  • page.path === '/blog/some-post'
  • page.params.slug === 'some-post'
  • page.query.get('foo') === 'bar'
  • page.query.has('baz')
  • page.query.getAll('bizz') === ['a', 'b']

fetch

fetch is equivalent to the native fetch web API, and can make credentialled requests. It can be used across both client and server contexts.

When fetch runs on the server, the resulting response will be serialized and inlined into the rendered HTML. This allows the subsequent client-side load to access identical data immediately without an additional network request.

session

session can be used to pass data from the server related to the current request, e.g. the current user. By default it is undefined. See getSession to learn how to use it.

context

context is passed from layout components to child layouts and page components. For the root $layout.svelte component, it is equal to {}, but if that component's load function returns an object with a context property, it will be available to subsequent load functions.

It is important to note that load may run on either the server or in the client browser. Code called inside load blocks:

  • should run on the same domain as any upstream API servers requiring credentials
  • should not reference window, document or any browser-specific objects
  • should not reference any API keys or secrets, which will be exposed to the client

Output

If you return a Promise from load, SvelteKit will delay rendering until the promise resolves. The return value has several properties, all optional:

status

The HTTP status code for the page. If returning an error this must be a 4xx or 5xx response; if returning a redirect it must be a 3xx response. The default is 200.

error

If something goes wrong during load, return an Error object or a string describing the error alongside a 4xx or 5xx status code.

redirect

If the page should redirect (because the page is deprecated, or the user needs to be logged in, or whatever else) return a string containing the location to which they should be redirected alongside a 3xx status code.

maxage

To cause pages to be cached, return a number describing the page's max age in seconds. The resulting cache header will include private if user data was involved in rendering the page (either via session, or because a credentialled fetch was made in a load function), but otherwise will include public so that it can be cached by CDNs.

This only applies to page components, not layout components.

props

If the load function returns a props object, the props will be passed to the component when it is rendered.

context

This will be merged with any existing context and passed to the load functions of subsequent layout and page components.

This only applies to layout components, not page components.

Hooks

An optional src/hooks.js (or src/hooks.ts, or src/hooks/index.js) file exports three functions, all optional, that run on the server — getContext, getSession and handle.

The location of this file can be configured as config.kit.files.hooks

getContext

This function runs on every incoming request. It generates the context object that is available to endpoint handlers as request.context, and used to derive the session object available in the browser.

If unimplemented, context is {}.

type Incoming = {
	method: string;
	host: string;
	headers: Headers;
	path: string;
	query: URLSearchParams;
	body: string | Buffer | ReadOnlyFormData;
};

type GetContext<Context = any> = {
	(incoming: Incoming): Context;
};
import * as cookie from 'cookie';
import db from '$lib/db';

/** @type {import('@sveltejs/kit').GetContext} */
export async function getContext({ headers }) {
	const cookies = cookie.parse(headers.cookie || '');

	return {
user: (await db.get_user(cookies.session_id)) || { guest: true }
	};
}

getSession

This function takes the context object and returns a session object that is safe to expose to the browser. It runs whenever SvelteKit renders a page.

If unimplemented, session is {}.

type GetSession<Context = any, Session = any> = {
	({ context }: { context: Context }): Session | Promise<Session>;
};
/** @type {import('@sveltejs/kit').GetSession} */
export function getSession({ context }) {
	return {
user: {
	// only include properties needed client-side —
	// exclude anything else attached to the user
	// like access tokens etc
	name: context.user?.name,
	email: context.user?.email,
	avatar: context.user?.avatar
}
	};
}

session must be serializable, which means it must not contain things like functions or custom classes, just built-in JavaScript data types

handle

This function runs on every request, and determines the response. It receives the request object and render method, which calls SvelteKit's default renderer. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing endpoints programmatically, for example).

If unimplemented, defaults to ({ request, render }) => render(request).

type Request<Context = any> = {
	method: string;
	host: string;
	headers: Headers;
	path: string;
	params: Record<string, string>;
	query: URLSearchParams;
	body: string | Buffer | ReadOnlyFormData;
	context: Context;
};

type Response = {
	status?: number;
	headers?: Headers;
	body?: any;
};

type Handle<Context = any> = ({
	request: Request<Context>,
	render: (request: Request<Context>) => Promise<Response>
}) => Response | Promise<Response>;
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ request, render }) {
	const response = await render(request);

	return {
...response,
headers: {
	...response.headers,
	'x-custom-header': 'potato'
}
	};
}

Modules

SvelteKit makes a number of modules available to your application.

$app/env

import { amp, browser, dev, prerendering } from '$app/env';
  • amp is true or false depending on the corresponding value in your project configuration
  • browser is true or false depending on whether the app is running in the browser or on the server
  • dev is true in development mode, false in production
  • prerendering is true when prerendering, false otherwise

$app/navigation

import { goto, prefetch, prefetchRoutes } from '$app/navigation';
  • goto(href, { replaceState, noscroll }) returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified href. The second argument is optional. If replaceState is true, a new history entry won't be created. If noscroll is true, the browser won't scroll to the top of the page after navigation.
  • prefetch(href) programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's load function with the appropriate options. This is the same behaviour that SvelteKit triggers when the user taps or mouses over an <a> element with sveltekit:prefetch. If the next navigation is to href, the values returned from load will be used, making navigation instantaneous. Returns a Promise that resolves when the prefetch is complete.
  • prefetchRoutes(routes) — programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this to speed up subsequent navigation. If no argument is given, all routes will be fetched, otherwise you can specify routes by any matching pathname such as /about (to match src/routes/about.svelte) or /blog/* (to match src/routes/blog/[slug].svelte). Unlike prefetch, this won't call preload for individual pages. Returns a Promise that resolves when the routes have been prefetched.

$app/paths

import { base, assets } from '$app/paths';
  • base — a root-relative (i.e. begins with a /) string that matches config.kit.files.base in your project configuration
  • assets — a root-relative or absolute path that matches config.kit.files.assets (after it has been resolved against base)

$app/stores

import { getStores, navigating, page, session } from '$app/stores';

Stores are contextual — they are added to the context or your root component. This means that session and page are unique to each request on the server, rather than shared between multiple requests handled by the same server simultaneously, which is what makes it safe to include user-specific data in session.

Because of that, the stores are not free-floating objects: they must be accessed during component initialisation, like anything else that would be accessed with getContext.

  • getStores is a convenience function around getContext that returns { navigating, page, session }. Most of the time, you won't need to use it.

The stores themselves attach to the correct context at the point of subscription, which means you can import and use them directly in components without boilerplate.

  • navigating is a readable store. When navigating starts, its value is { from, to }, where from and to both mirror the page store value. When navigating finishes, its value reverts to null.
  • page is a readable store whose value reflects the object passed to load functions — it contains host, path, params and query. See the page section above for more details.
  • session is a writable store whose initial value is whatever was returned from getSession. It can be written to, but this will not cause changes to persist on the server — this is something you must implement yourself.

$lib

This is a simple alias to src/lib, or whatever directory is specified as [config.kit.files.lib]. It allows you to access common components and utility modules without ../../../../ nonsense.

$service-worker

This module is only available to service workers.

import { build, files, timestamp } from '$service-worker';
  • build is an array of URL strings representing the files generated by Vite, suitable for caching with cache.addAll(build)
  • files is an array of URL strings representing the files in your static directory, or whatever directory is specified by config.kit.files.assets
  • timestamp is the result of calling Date.now() at build time. It's useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches

Service workers

Service workers act as proxy servers that handle network requests inside your app. This makes it possible to make your app work offline, but even if you don't need offline support (or can't realistically implement it because of the type of app you're building), it's often worth using service workers to speed up navigation by precaching your built JS and CSS.

In SvelteKit, if you have a src/service-worker.js file (or src/service-worker.ts, or src/service-worker/index.js, etc) it will be built with Vite and automatically registered.

You can change the location of your service worker in your project configuration.

Inside the service worker you have access to the $service-worker module.

Because it needs to be bundled (since browsers don't yet support import in this context), and depends on the client-side app's build manifest, service workers only work in the production build, not in development. To test it locally, use svelte-kit start.

Anchor options

sveltekit:prefetch

SvelteKit uses code splitting to break your app into small chunks (one per route), ensuring fast startup times.

For dynamic routes, such as our src/routes/blog/[slug].svelte example, that's not enough. In order to render the blog post, we need to fetch the data for it, and we can't do that until we know what slug is. In the worst case, that could cause lag as the browser waits for the data to come back from the server.

We can mitigate that by prefetching the data. Adding a sveltekit:prefetch attribute to a link...

<a sveltekit:prefetch href="blog/what-is-sveltekit">What is SvelteKit?</a>

...will cause SvelteKit to run the page's load function as soon as the user hovers over the link (on a desktop) or touches it (on mobile), rather than waiting for the click event to trigger navigation. Typically, this buys us an extra couple of hundred milliseconds, which is the difference between a user interface that feels laggy, and one that feels snappy.

Note that prefetching will not work if the router setting is false.

You can also programmatically invoke prefetch from $app/navigation.

sveltekit:noscroll

When navigating to internal links, SvelteKit mirrors the browser's default navigation behaviour: it will change the scroll position to 0,0 so that the user is at the very top left of the page (unless the link includes a #hash, in which case it will scroll to the element with a matching ID).

In certain cases, you may wish to disable this behaviour. Adding a sveltekit:noscroll attribute to a link...

<a href="path" sveltekit:noscroll>Path</a>

...will prevent scrolling after the link is clicked.

rel=external

By default, the SvelteKit runtime intercepts clicks on <a> elements and bypasses the normal browser navigation for relative (same-origin) URLs that match one of your page routes. We sometimes need to tell SvelteKit that certain links need to be handled by normal browser navigation.

Adding a rel=external attribute to a link...

<a rel="external" href="path">Path</a>

...will trigger a browser navigation when the link is clicked.

Events

SvelteKit emits CustomEvents on the window object when certain things happen:

  • sveltekit:start — fired once the app has hydrated
  • sveltekit:navigation-start — navigation has started
  • sveltekit:navigation-end — navigation has ended

You probably won't need to use these, but they can be useful in the context of (for example) integration tests.

AMP

An unfortunate reality of modern web development is that it is sometimes necessary to create an AMP version of your site. In SvelteKit this can be done by setting the amp config option, which has the following effects:

  • Client-side JavaScript, including the router, is disabled
  • Styles are concatenated into <style amp-custom>, and the AMP boilerplate is injected
  • In development, requests are checked against the AMP validator so you get early warning of any errors

Adapters

Before you can deploy your SvelteKit app, you need to adapt it for your deployment target. Adapters are small plugins that take the built app as input and generate output that is optimised for a specific platform.

For example, if you want to run your app as a simple Node server, you would use the @sveltejs/adapter-node package:

// svelte.config.cjs
const node = require('@sveltejs/adapter-node');

module.exports = {
	kit: {
adapter: node()
	}
};

With this, svelte-kit build will generate a self-contained Node app inside build. You can pass options to adapters, such as customising the output directory in adapter-node:

// svelte.config.cjs
const node = require('@sveltejs/adapter-node');

module.exports = {
	kit: {
-        adapter: node()
+        adapter: node({ out: 'my-output-directory' })
	}
};

A variety of official adapters exist for serverless platforms...

...and others:

  • adapter-node — for creating self-contained Node apps
  • adapter-static — for prerendering your entire site as a collection of static files

The adapter API is still in flux and will likely change before 1.0.

SSR and JavaScript

By default, SvelteKit will server-render pages on demand, then hydrate the rendered HTML in the client with an interactive Svelte app while initialising a router that takes over subsequent navigations.

You can control each of these on a per-app or per-page basis. Note that each of the per-page settings use context="module", and only apply to page components, not layout components.

If both are specified, per-page settings override per-app settings in case of conflicts. Each setting can be controlled independently, but ssr and hydrate cannot both be false since that would result in nothing being rendered at all.

ssr

Disabling server-side rendering effectively turns your SvelteKit app into a single-page app or SPA.

In most situations this is not recommended: it harms SEO, tends to slow down perceived performance, and makes your app inaccessible to users if JavaScript fails or is disabled (which happens more often than you probably think). Sometimes it's appropriate or even necessary, but consider alternatives before disabling SSR.

You can disable SSR app-wide with the ssr config option, or a page-level ssr export:

<script context="module">
	export const ssr = false;
</script>

router

SvelteKit includes a client-side router that intercepts navigations (from the user clicking on links, or interacting with the back/forward buttons) and updates the page contents, rather than letting the browser handle the navigation by reloading.

In certain circumstances you might need to disable this behaviour with the app-wide router config option or the page-level router export:

<script context="module">
	export const router = false;
</script>

Note that this will disable client-side routing for any navigation from this page, regardless of whether the router is already active.

hydrate

Ordinarily, SvelteKit 'hydrates' your server-rendered HTML into an interactive page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this category. In these cases you can skip hydration when the app boots up with the app-wide hydrate config option or the page-level hydrate export:

<script context="module">
	export const hydrate = false;
</script>

If hydrate and router are both false, SvelteKit will not add any JavaScript to the page at all.

prerender

It's likely that at least some pages of your app can be represented as a simple HTML file, since they contain no dynamic or user-specific data. These pages can be prerendered by your adapter.

If your entire app is suitable for prerendering, you could use adapter-static, which will generate HTML files for every page, plus additional files that are requested by load functions in those pages.

In many cases, you'll only want to prerender specific pages in your app. You'll need to annotate these pages:

<script context="module">
	export const prerender = true;
</script>

The prerenderer will start at the root of your app and generate HTML for any prerenderable pages it finds. Each page is scanned for <a> elements that point to other pages that are candidates for prerendering — because of this, you generally don't need to specify which pages should be accessed. If you do need to specify which pages should be accessed by the prerenderer, you can do so with the pages option in the prerender configuration.

When not to prerender

The basic rule is this: for a page to be prerenderable, any two users hitting it directly must get the same content from the server.

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.

Note that you can still prerender pages that load data based on the page's parameters, like our src/routes/blog/[slug].svelte example from earlier. The prerenderer will intercept requests made inside load, so the data served from src/routes/blog/[slug].json.js will also be captured.

Route conflicts

Because prerendering writes to the filesystem, it isn't possible to have two endpoints that would cause a directory and a file to have the same name. For example, src/routes/foo/index.js and src/routes/foo/bar.js would try to create foo and foo/bar, which is impossible.

For that reason among others, it's recommended that you always include a file extension — src/routes/foo/index.json.js and src/routes/foo/bar.json.js would result in foo.json and foo/bar.json files living harmoniously side-by-side.

For pages, we skirt around this problem by writing foo/index.html instead of foo.

Command Line Interface

SvelteKit includes a command line interface for building and running your app.

In the default project template svelte-kit dev, svelte-kit build and svelte-kit start are aliased as npm run dev, npm run build and npm start respectively. You can also invoke the CLI with npx:

npx svelte-kit dev

svelte-kit dev

Starts a development server. It accepts the following options:

  • -p/--port — which port to start the server on
  • -o/--open — open a browser tab once the server starts
  • -h/--host — expose the server to the network. This will allow people using the same coffee shop WiFi as you to see files on your computer; use it with care
  • -H/--https — launch an HTTPS server using a self-signed certificate. Useful for testing HTTPS-only features on an external device

svelte-kit build

Builds a production version of your app, and runs your adapter if you have one specified in your config. It accepts the following option:

  • --verbose — log more detail

svelte-kit start

After you've built your app with svelte-kit build, you can start the production version (irrespective of any adapter that has been applied) locally with svelte-kit start. This is intended for testing the production build locally, not for serving your app, for which you should always use an adapter.

Like svelte-kit dev, it accepts the following options:

  • -p/--port
  • -o/--open
  • -h/--host (note the security caveat above)
  • -H/--https

Configuration

Your project's configuration lives in a svelte.config.cjs file. All values are optional. The complete list of options, with defaults, is shown here:

/** @type {import('@sveltejs/kit').Config} */
module.exports = {
	// options passed to svelte.compile (https://svelte.dev/docs#svelte_compile)
	compilerOptions: null,

	// an array of file extensions that should be treated as Svelte components
	extensions: ['.svelte'],

	kit: {
adapter: null,
amp: false,
appDir: '_app',
files: {
	assets: 'static',
	hooks: 'src/hooks',
	lib: 'src/lib',
	routes: 'src/routes',
	serviceWorker: 'src/service-worker',
	template: 'src/app.html'
},
host: null,
hostHeader: null,
hydrate: true,
paths: {
	assets: '',
	base: ''
},
prerender: {
	crawl: true,
	enabled: true,
	force: false,
	pages: ['*']
},
router: true,
ssr: true,
target: null,
vite: () => ({})
	},

	// options passed to svelte.preprocess (https://svelte.dev/docs#svelte_preprocess)
	preprocess: null
};

adapter

Determines how the output of svelte-kit build is converted for different platforms. See Adapters.

amp

Enable AMP mode.

appDir

The directory relative to paths.assets where the built JS and CSS (and imported assets) are served from. (The filenames therein contain content-based hashes, meaning they can be cached indefinitely).

files

An object containing zero or more of the following string values:

  • assets — a place to put static files that should have stable URLs and undergo no processing, such as favicon.ico or manifest.json
  • lib — your app's internal library, accessible throughout the codebase as $lib
  • routes — the files that define the structure of your app (see Routing)
  • serviceWorker — the location of your service worker's entry point (see Service workers)
  • hooks — the location of your hooks module (see Hooks)
  • template — the location of the template for HTML responses

host

A value that overrides the Host header when populating page.host

hostHeader

If your app is behind a reverse proxy (think load balancers and CDNs) then the Host header will be incorrect. In most cases, the underlying host is exposed via the X-Forwarded-Host header and you should specify this in your config if you need to access page.host:

// svelte.config.cjs
module.exports = {
	kit: {
hostHeader: 'X-Forwarded-Host'
	}
};

You should only do this if you trust the reverse proxy, which is why it isn't the default.

hydrate

Whether to hydrate the server-rendered HTML with a client-side app. (It's rare that you would set this to false on an app-wide basis.)

paths

An object containing zero or more of the following string values:

  • assets — an absolute path, or a path relative to base, where your app's files are served from. This is useful if your files are served from a storage bucket of some kind
  • base — a root-relative (i.e. starts with /) path that specifies where your app is served from. This allows the app to live on a non-root path

prerender

See Prerendering. An object containing zero or more of the following:

  • crawl — determines whether SvelteKit should find pages to prerender by following links from the seed page(s)
  • enabled — set to false to disable prerendering altogether
  • force — if true, a page that fails to render will not cause the entire build to fail
  • pages — an array of pages to prerender, or start crawling from (if crawl: true). The * string includes all non-dynamic routes (i.e. pages with no [parameters] )

router

Enables or disables the client-side router app-wide.

ssr

Enables or disables server-side rendering app-wide.

target

Specifies an element to mount the app to. It must be a DOM selector that identifies an element that exists in your template file. If unspecified, the app will be mounted to document.body.

vite

A Vite config object, or a function that returns one. Not all configuration options can be set, since SvelteKit depends on certain values being configured internally.

Troubleshooting

Server-side rendering

SvelteKit will render any component first on the server side and send it to the client as HTML. It will then run the component again on the client side to allow it to update based on dynamic data. This means you need to ensure that components can run both on the client and server side.

If, for example, your components try to access the global variables document or window, this will result in an error when the page is pre-rendered on the server side.

If you need access to these variables, you can run code exclusively on the client side by wrapping it in

import { browser } from '$app/env';

if (browser) {
	// client-only code here
}

Alternatively, you can run it onMount, since this only runs in the browser. This is also a good way to load libraries that depend on window:

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

	let awkward;

	onMount(async () => {
const module = await import('some-browser-only-library');
awkward = module.default;
	});
</script>