Introduction permalink

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.

Building an app with all the modern best practices is fiendishly complicated. Those practices include build optimizations, so that you load only the minimal required code; offline support; prefetching pages before the user initiates navigation; and configurable rendering that allows you to generate HTML on the server or in the browser at runtime or at build-time. SvelteKit does all the boring stuff for you so that you can get on with the creative part.

It uses Vite with a Svelte plugin to provide a lightning-fast and feature-rich development experience with Hot Module Replacement (HMR), where changes to your code are reflected in the browser instantly.

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 introduction to Svelte blog post and the Svelte 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

The first command will scaffold a new project in the my-app directory asking you if you'd like to setup some basic tooling such as TypeScript. See the FAQ for pointers on setting up additional tooling. The subsequent commands will then install its dependencies and start a server on localhost:3000.

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

Try editing the files to get a feel for how everything works – you may not need to bother reading the rest of this guide!

Editor setup

We recommend using Visual Studio Code (aka VS Code) with the Svelte extension, but support also exists for numerous other editors.

Routing permalink

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 behaviour 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 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 -->

<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 -->

<h1>About this site</h1>

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.

A file or directory can have multiple dynamic parts, like [id]-[category].svelte. (Parameters are 'non-greedy'; in an ambiguous case like x-y-z, id would be x and category would be y-z.)


Endpoints are modules written in .js (or .ts) files that export functions corresponding to HTTP methods.

// Declaration types for Endpoints
// * declarations that are not exported are for internal use

export interface RequestEvent<Locals = Record<string, any>> {
	request: Request;
	url: URL;
	params: Record<string, string>;
	locals: Locals;

type Body = JSONString | Uint8Array | ReadableStream | stream.Readable;
export interface EndpointOutput {
	status?: number;
	headers?: HeadersInit;
	body?: Body;

type MaybePromise<T> = T | Promise<T>;
interface Fallthrough {
	fallthrough: true;
export interface RequestHandler<Locals = Record<string, any>> {
	(event: RequestEvent<Locals>): MaybePromise<Either<Response | EndpointOutput, Fallthrough>>;

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:

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 article = await db.get(params.slug);

	if (article) {
		return {
			body: {

	return {
		status: 404

All server-side code, including endpoints, has access to fetch in case you need to request data from external APIs.

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

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.)

If {fallthrough: true} is returned SvelteKit will fall through to other routes until something responds, or will respond with a generic 404.

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

export function post(event) {...}

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.

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

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

Body parsing

The request object is an instance of the standard Request class. As such, accessing the request body is easy:

export async function post({ request }) {
	const data = await request.formData(); // or .json(), or .text(), etc

HTTP Method Overrides

HTML <form> elements only support GET and POST methods natively. You can allow other methods, like PUT and DELETE, by specifying them in your configuration and adding a _method=VERB parameter (you can configure the name) to the form's action:

// svelte.config.js
export default {
	kit: {
		methodOverride: {
			allowed: ['PUT', 'PATCH', 'DELETE']
<form method="post" action="/todos/{id}?_method=PUT">
	<!-- form elements -->

Using native <form> behaviour ensures your app continues to work when JavaScript fails or is disabled.

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.


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] which case a request for /sveltejs/kit/tree/master/documentation/docs/ would result in the following parameters being available to the page:

	org: 'sveltejs',
	repo: 'kit',
	branch: 'master',
	file: 'documentation/docs/'

src/routes/a/[]/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...


... and you navigate to /foo-xyz, then SvelteKit will first try foo-[bar].svelte because it is the best match. If that yields no response, SvelteKit will try other less specific yet still valid matches for /foo-xyz. Since endpoints have higher precedence than pages, the next attempt will be [baz].js. Then alphabetical order takes precedence and thus [baz].svelte will be tried before [qux].svelte. 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 permalink

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...


...but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a <slot> for the page content. For example, let's add a nav bar:

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


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

<!-- src/routes/index.svelte -->
<!-- src/routes/about.svelte -->
<!-- src/routes/settings.svelte -->

...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

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 -->

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



To reset the layout stack, create a __layout.reset.svelte file instead of a __layout.svelte file. For example, if you want your /admin/* pages to not inherit the root layout, create a file called src/routes/admin/__layout.reset.svelte.

Layout resets are otherwise identical to normal layout components.

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 recommended that you bring your own.

// declaration type
// * also see type for `LoadOutput` in the Loading section

export interface ErrorLoadInput<
	PageParams extends Record<string, string> = Record<string, string>,
	Stuff extends Record<string, any> = Record<string, any>,
	Session = any
> extends LoadInput<PageParams, Stuff, Session> {
	status?: number;
	error?: Error;

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

<script context="module">
	/** @type {import('@sveltejs/kit').ErrorLoad} */
	export function load({ error, status }) {
		return {
			props: {
				title: `${status}: ${error.message}`

	export let title;


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

Loading permalink

A component that defines a page or a layout 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.

// Declaration types for Loading
// * declarations that are not exported are for internal use

export interface LoadInput<
	PageParams extends Record<string, string> = Record<string, string>,
	Stuff extends Record<string, any> = Record<string, any>,
	Session = any
> {
	url: URL;
	params: PageParams;
	fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
	session: Session;
	stuff: Stuff;

export interface LoadOutput<
	Props extends Record<string, any> = Record<string, any>,
	Stuff extends Record<string, any> = Record<string, any>
> {
	status?: number;
	error?: string | Error;
	redirect?: string;
	props?: Props;
	stuff?: Stuff;
	maxage?: number;

interface LoadInputExtends {
	stuff?: Record<string, any>;
	pageParams?: Record<string, string>;
	session?: any;
interface LoadOutputExtends {
	stuff?: Record<string, any>;
	props?: Record<string, any>;

type MaybePromise<T> = T | Promise<T>;
interface Fallthrough {
	fallthrough: true;
export interface Load<
	Input extends LoadInputExtends = Required<LoadInputExtends>,
	Output extends LoadOutputExtends = Required<LoadOutputExtends>
> {
		input: LoadInput<
			InferValue<Input, 'pageParams', Record<string, string>>,
			InferValue<Input, 'stuff', Record<string, any>>,
			InferValue<Input, 'session', any>
	): MaybePromise<
				InferValue<Output, 'props', Record<string, any>>,
				InferValue<Output, 'stuff', Record<string, any>>

Our example blog page might contain a load function like the following:

<script context="module">
	/** @type {import('@sveltejs/kit').Load} */
	export async function load({ params, fetch, session, stuff }) {
		const url = `/blog/${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}`)

Note the <script context="module"> — this is necessary because load runs before the component is rendered. Code that is per-component instance should go into a second <script> tag.

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

If load returns {fallthrough: true}, SvelteKit will fall through to other routes until something responds, or will respond with a generic 404.

SvelteKit's load receives an implementation of fetch, which has the following special properties:

  • it has access to cookies on the server
  • it can make requests against the app's own endpoints without issuing an HTTP call
  • it makes a copy of the response when you use it, and then sends it embedded in the initial page load for hydration

load only applies to page and layout components (not components they import), and runs on both the server and in the browser with the default rendering options.

Code called inside load blocks:

  • should use the SvelteKit-provided fetch wrapper rather than using the native fetch
  • should not reference window, document, or any browser-specific objects
  • should not directly reference any API keys or secrets, which will be exposed to the client, but instead call an endpoint that uses any required secrets

It is recommended that you not store pre-request state in global variables, but instead use them only for cross-cutting concerns such as caching and holding database connections.

Mutating any shared state on the server will affect all clients, not just the current one.


The load function receives an object containing five fields — url, params, fetch, session and stuff. The load function is reactive, and will re-run when its parameters change, but only if they are used in the function. Specifically, if url, session or stuff are used in the function, they will be re-run whenever their value changes, and likewise for the individual properties of params.

Note that destructuring parameters in the function declaration is enough to count as using them.


url is an instance of URL, containing properties like the origin, hostname, pathname and searchParams.

In some environments this is derived from request headers, which you may need to configure, during server-side rendering


params is derived from url.pathname and the route filename.

For a route filename example like src/routes/a/[b]/[...c] and a url.pathname of /a/x/y/z, the params object would look like this:

	"b": "x",
	"c": "y/z"


fetch is equivalent to the native fetch web API, and can make credentialed 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.

Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.


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.


stuff is passed from layout components to child layouts and page components and can be filled with anything else you need to make available. For the root __layout.svelte component, it is equal to {}, but if that component's load function returns an object with a stuff property, it will be available to subsequent load functions.


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


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.


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


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.

The redirect string should be a properly encoded URI. Both absolute and relative URIs are acceptable.


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 credentialed 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.


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


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

The combined stuff is available to components using the page store as $page.stuff, providing a mechanism for pages to pass data 'upward' to layouts.

Hooks permalink

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

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


This function runs every time SvelteKit receives a request — whether that happens while the app is running, or during prerendering — and determines the response. It receives an event object representing the request and a function called resolve, which invokes SvelteKit's router and generates a response (rendering a page, or invoking an endpoint) accordingly. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing endpoints programmatically, for example).

Requests for static assets — which includes pages that were already prerendered — are not handled by SvelteKit.

If unimplemented, defaults to ({ event, resolve }) => resolve(event).

// Declaration types for Hooks
// * declarations that are not exported are for internal use

// type of string[] is only for set-cookie
// everything else must be a type of string
type ResponseHeaders = Record<string, string | string[]>;

export interface RequestEvent<Locals = Record<string, any>> {
	request: Request;
	url: URL;
	params: Record<string, string>;
	locals: Locals;

export interface ResolveOpts {
	ssr?: boolean;

export interface Handle<Locals = Record<string, any>> {
	(input: {
		event: RequestEvent<Locals>;
		resolve(event: RequestEvent<Locals>, opts?: ResolveOpts): MaybePromise<Response>;
	}): MaybePromise<Response>;

To add custom data to the request, which is passed to endpoints, populate the event.locals object, as shown below.

/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
	event.locals.user = await getUserInformation(event.request.headers.get('cookie'));

	const response = await resolve(event);
	response.headers.set('x-custom-header', 'potato');

	return response;

You can add call multiple handle functions with the sequence helper function.

resolve also supports a second, optional parameter that gives you more control over how the response will be rendered. That parameter is an object that can have the following fields:

  • ssr — specifies whether the page will be loaded and rendered on the server.
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ request, resolve }) {
	const response = await resolve(request, {
		ssr: !request.url.pathname.startsWith('/admin')

	return response;

Disabling server-side rendering effectively turns your SvelteKit app into a single-page app or SPA. In most situations this is not recommended (see appendix). Consider whether it's truly appropriate to disable it, and do so selectively rather than for all requests.


If an error is thrown during rendering, this function will be called with the error and the request that caused it. This allows you to send data to an error tracking service, or to customise the formatting before printing the error to the console.

During development, if an error occurs because of a syntax error in your Svelte code, a frame property will be appended highlighting the location of the error.

If unimplemented, SvelteKit will log the error with default formatting.

// Declaration types for handleError hook
export interface HandleError<Locals = Record<string, any>> {
	(input: { error: Error & { frame?: string }; event: RequestEvent<Locals> }): void;
/** @type {import('@sveltejs/kit').HandleError} */
export async function handleError({ error, event }) {
	// example integration with
	Sentry.captureException(error, { event });

handleError is only called in the case of an uncaught exception. It is not called when pages and endpoints explicitly respond with 4xx and 5xx status codes.


This function takes the event object and returns a session object that is accessible on the client and therefore must be safe to expose to users. It runs whenever SvelteKit server-renders a page.

If unimplemented, session is {}.

// Declaration types for getSession hook
export interface GetSession<Locals = Record<string, any>, Session = any> {
	(event: RequestEvent<Locals>): Session | Promise<Session>;
/** @type {import('@sveltejs/kit').GetSession} */
export function getSession(event) {
	return event.locals.user
		? {
				user: {
					// only include properties needed client-side —
					// exclude anything else attached to the user
					// like access tokens etc
					avatar: event.locals.user.avatar
		: {};

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


This function allows you to modify (or replace) a fetch request for an external resource that happens inside a load function that runs on the server (or during pre-rendering).

For example, your load function might make a request to a public URL like when the user performs a client-side navigation to the respective page, but during SSR it might make sense to hit the API directly (bypassing whatever proxies and load balancers sit between it and the public internet).

// Declaration types for externalFetch hook

export interface ExternalFetch {
	(req: Request): Promise<Response>;
/** @type {import('@sveltejs/kit').ExternalFetch} */
export async function externalFetch(request) {
	if (request.url.startsWith('')) {
		// clone the original request, but change the URL
		request = new Request(
			request.url.replace('', 'http://localhost:9999/'),

	return fetch(request);

Modules permalink

SvelteKit makes a number of modules available to your application.


import { amp, browser, dev, mode, 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
  • mode is the Vite mode, which is development in dev mode or production during build unless configured otherwise in config.kit.vite.mode.
  • prerendering is true when prerendering, false otherwise


import {
} from '$app/navigation';
  • afterNavigate(({ from, to }: { from: URL, to: URL }) => void) - a lifecycle function that runs when the components mounts, and after subsequent navigations while the component remains mounted
  • beforeNavigate(({ from, to, cancel }: { from: URL, to: URL | null, cancel: () => void }) => void) — a function that runs whenever navigation is triggered whether by clicking a link, calling goto, or using the browser back/forward controls. This includes navigation to external sites. to will be null if the user is closing the page. Calling cancel will prevent the navigation from proceeding
  • disableScrollHandling will, if called when the page is being updated following a navigation (in onMount or an action, for example), prevent SvelteKit from applying its normal scroll management. You should generally avoid this, as breaking user expectations of scroll behaviour can be disorienting.
  • goto(href, { replaceState, noscroll, keepfocus, state }) 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:
    • replaceState (boolean, default false) If true, will replace the current history entry rather than creating a new one with pushState
    • noscroll (boolean, default false) If true, the browser will maintain its scroll position rather than scrolling to the top of the page after navigation
    • keepfocus (boolean, default false) If true, the currently focused element will retain focus after navigation. Otherwise, focus will be reset to the body
    • state (object, default {}) The state of the new/updated history entry
  • invalidate(href) causes any load functions belonging to the currently active page to re-run if they fetch the resource in question. It returns a Promise that resolves when the page is subsequently updated.
  • 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 load for individual pages. Returns a Promise that resolves when the routes have been prefetched.


import { base, assets } from '$app/paths';
  • base — a root-relative (i.e. begins with a /) string that matches config.kit.paths.base, or the empty string if unspecified
  • assets — an absolute URL that matches config.kit.paths.assets, if specified, otherwise equal to base

If a value for config.kit.paths.assets is specified, it will be replaced with '/_svelte_kit_assets' during svelte-kit dev or svelte-kit preview, since the assets don't yet live at their eventual URL.


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

Stores are contextual — they are added to the context of 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 }. This needs to be called at the top-level or synchronously during component or page initialisation.

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. However, it still needs to be called synchronously on component or page initialisation when the $-prefix isn't used. Use getStores to safely .subscribe asynchronously instead.

  • navigating is a readable store. When navigating starts, its value is { from, to }, where from and to are both URL instances. When navigating finishes, its value reverts to null.
  • page contains an object with the current url, params and stuff.
  • 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.


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.


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. You can customize which files are included from static directory using config.kit.serviceWorker.files
  • timestamp is the result of calling 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


This module provides a helper function to sequence multiple handle calls.

import { sequence } from '@sveltejs/kit/hooks';

async function first({ event, resolve }) {
	return await resolve(event);
async function second({ event, resolve }) {
	return await resolve(event);

export const handle = sequence(first, second);

Service workers permalink

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 disable automatic registration if you need to register the service worker with your own logic (e.g. prompt user for update, configure periodic updates, use workbox, etc).

You can change the location of your service worker and disable automatic registration 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 preview.

Anchor options permalink


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.


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.


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. Examples of this might be linking to another page on your domain that's not part of your SvelteKit app or linking to an endpoint.

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 permalink

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 permalink

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 permalink

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 for deployment.

By default, projects are configured to use @sveltejs/adapter-auto, which detects your production environment and selects the appropriate adapter where possible. If your platform isn't (yet) supported, you may need to install a custom adapter or write one.

See the adapter-auto README for information on adding support for new environments.

Supported environments

SvelteKit offers a number of officially-supported adapters.

The following platforms require no additional configuration:


To create a simple Node server, install the @sveltejs/adapter-node@next package and update your svelte.config.js:

// svelte.config.js
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-node';

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

// svelte.config.js
import adapter from '@sveltejs/adapter-node';

export default {
	kit: {
-		adapter: adapter()
+		adapter: adapter({ out: 'my-output-directory' })

Static sites

Most adapters will generate static HTML for any prerenderable pages of your site. In some cases, your entire app might be prerenderable, in which case you can use @sveltejs/adapter-static@next to generate static HTML for all your pages. A fully static site can be hosted on a wide variety of platforms, including static hosts like GitHub Pages.

// svelte.config.js
-import adapter from '@sveltejs/adapter-auto';
+import adapter from '@sveltejs/adapter-static';

You can also use adapter-static to generate single-page apps (SPAs) by specifying a fallback page.

Community adapters

Additional community-provided adapters exist for other platforms. After installing the relevant adapter with your package manager, update your svelte.config.js:

// svelte.config.js
-import adapter from '@sveltejs/adapter-auto';
+import adapter from 'svelte-adapter-[x]';

Writing custom adapters

We recommend looking at the source for an adapter to a platform similar to yours and copying it as a starting point.

Adapters packages must implement the following API, which creates an Adapter:

/** @param {AdapterSpecificOptions} options */
export default function (options) {
	/** @type {import('@sveltejs/kit').Adapter} */
	return {
		name: 'adapter-package-name',
		async adapt(builder) {
			// adapter implementation

The types for Adapter and its parameters are available in types/config.d.ts.

Within the adapt method, there are a number of things that an adapter should do:

  • Clear out the build directory
  • Call builder.prerender({ dest }) to prerender pages
  • Output code that:
    • Imports App from ${builder.getServerDirectory()}/app.js
    • Instantiates the app with a manifest generated with builder.generateManifest({ relativePath })
    • Listens for requests from the platform, converts them to a standard Request if necessary, calls the render function to generate a Response and responds with it
    • Globally shims fetch to work on the target platform, if necessary. SvelteKit provides a @sveltejs/kit/install-fetch helper for platforms that can use node-fetch
  • Bundle the output to avoid needing to install dependencies on the target platform, if necessary
  • Put the user's static files and the generated JS/CSS in the correct location for the target platform

Where possible, we recommend putting the adapter output under the build/ directory with any intermediate output placed under .svelte-kit/[adapter-name].

The adapter API may change before 1.0.

Page options permalink

By default, SvelteKit will render any component first on the server and send it to the client as HTML. It will then render the component again in the browser to make it interactive in a process called hydration. For this reason, you need to ensure that components can run in both places. SvelteKit will then initialise 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.


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 client-side routing with the app-wide router config option or the page-level router export:

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

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


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;

If hydrate and router are both false, SvelteKit will not add any JavaScript to the page at all. If server-side rendering is disabled in handle, hydrate must be true or no content will be rendered.


It's likely that at least some pages of your app can be represented as a simple HTML file generated at build time. 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;

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 entries 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.

Not all pages are suitable for prerendering. Any content that is prerendered will be seen by all users. You can of course fetch personalized data in onMount in a prerendered page, but this may result in a poorer user experience since it will involve blank initial content or loading indicators.

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.

Accessing page.query during prerendering is forbidden. If you need to use it, ensure you are only doing so in the browser (for example in onMount).

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.

Packaging permalink

You can use SvelteKit to build component libraries as well as apps.

When you're creating an app, the contents of src/routes is the public-facing stuff; src/lib contains your app's internal library.

A SvelteKit component library has the exact same structure as a SvelteKit app, except that src/lib is the public-facing bit. src/routes might be a documentation or demo site that accompanies the library, or it might just be a sandbox you use during development.

Running svelte-kit package will take the contents of src/lib and generate a package directory (which can be configured) containing the following:

  • All the files in src/lib, unless you configure custom include/exclude options. Svelte components will be preprocessed, TypeScript files will be transpiled to JavaScript.
  • Type definitions (d.ts files) which are generated for Svelte, JavaScript and TypeScript files. You need to install typescript >= 4.0.0 and svelte2tsx >= 0.4.1 for this. Type definitions are placed next to their implementation, hand-written d.ts files are copied over as is. You can disable generation, but we strongly recommend against it.
  • A package.json copied from the project root with all fields but the "scripts" field. The "dependencies" field is included, which means you should add packages that you only need for your documentation or demo site to "devDependencies". A "type": "module" and an "exports" field will be added if it's not defined in the original file.

The "exports" field contains the package's entry points. By default, all files in src/lib will be treated as an entry point unless they start with (or live in a directory that starts with) an underscore, but you can configure this behaviour. If you have a src/lib/index.js or src/lib/index.svelte file, it will be treated as the package root.

For example, if you had a src/lib/Foo.svelte component and a src/lib/index.js module that re-exported it, a consumer of your library could do either of the following:

import { Foo } from 'your-library';
import Foo from 'your-library/Foo.svelte';


To publish the generated package:

npm publish ./package

The ./package above is referring to the directory name generated, change accordingly if you configure a custom package.dir.


This is a relatively experimental feature and is not yet fully implemented. All files except Svelte files (preprocessed) and TypeScript files (transpiled to JavaScript) are copied across as-is.

Command Line Interface permalink

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 preview are aliased as npm run dev, npm run build and npm run preview 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.
  • -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

After building the app, you can reference the documentation of your chosen adapter and hosting platform for specific instructions on how to serve your app.

svelte-kit preview

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 preview. 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
  • -H/--https

svelte-kit package

For package authors, see packaging.

Configuration permalink

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

/** @type {import('@sveltejs/kit').Config} */
const config = {
	// options passed to 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'
		floc: false,
		hydrate: true,
		inlineStyleThreshold: 0,
		methodOverride: {
			parameter: '_method',
			allowed: []
		package: {
			dir: 'package',
			emitTypes: true,
			// excludes all .d.ts and files starting with _ as the name
			exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath),
			files: () => true
		paths: {
			assets: '',
			base: ''
		prerender: {
			concurrency: 1,
			crawl: true,
			enabled: true,
			entries: ['*'],
			onError: 'fail'
		router: true,
		serviceWorker: {
			register: true,
			files: (filepath) => !/\.DS_STORE/.test(filepath)
		target: null,
		trailingSlash: 'never',
		vite: () => ({})

	// SvelteKit uses vite-plugin-svelte. Its options can be provided directly here.
	// See the available options at

	// options passed to svelte.preprocess (
	preprocess: null

export default config;


Required when running svelte-kit build and determines how the output is converted for different platforms. See Adapters.


Enable AMP mode.


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). Must not start or end with /.


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
  • hooks — the location of your hooks module (see Hooks)
  • 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)
  • template — the location of the template for HTML responses


Google's FLoC is a technology for targeted advertising that the Electronic Frontier Foundation has deemed harmful to user privacy. Browsers other than Chrome have declined to implement it.

In common with services like GitHub Pages, SvelteKit protects your users by automatically opting out of FLoC. It adds the following header to responses unless floc is true:

Permissions-Policy: interest-cohort=()

This only applies to server-rendered responses — headers for prerendered pages (e.g. created with adapter-static) are determined by the hosting platform.


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.)


Inline CSS inside a <style> block at the head of the HTML. This option is a number that specifies the maximum length of a CSS file to be inlined. All CSS files needed for the page and smaller than this value are merged and inlined in a <style> block.

This results in fewer initial requests and can improve your First Contentful Paint score. However, it generates larger HTML output and reduces the effectiveness of browser caches. Use it advisedly.


See HTTP Method Overrides. An object containing zero or more of the following:

  • parameter — query parameter name to use for passing the intended method value
  • allowed - array of HTTP methods that can be used when overriding the original request method


Options related to creating a package.

  • dir - output directory
  • emitTypes - by default, svelte-kit package will automatically generate types for your package in the form of .d.ts files. While generating types is configurable, we believe it is best for the ecosystem quality to generate types, always. Please make sure you have a good reason when setting it to false (for example when you want to provide handwritten type definitions instead)
  • exports - a function with the type of (filepath: string) => boolean. When true, the filepath will be included in the exports field of the package.json. Any existing values in the package.json source will be merged with values from the original exports field taking precedence
  • files - a function with the type of (filepath: string) => boolean. When true, the file will be processed and copied over to the final output folder, specified in dir

For advanced filepath matching, you can use exports and files options in conjunction with a globbing library:

// svelte.config.js
import mm from 'micromatch';

export default {
	kit: {
		package: {
			exports: (filepath) => {
				if (filepath.endsWith('.d.ts')) return false;
				return mm.isMatch(filepath, ['!**/_*', '!**/internal/**']);
			files: mm.matcher('!**/build.*')


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

  • assets — an absolute path that 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 path that must start, but not end with / (e.g. /base-path). This specifies where your app is served from and allows the app to live on a non-root path


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

  • concurrency — how many pages can be prerendered simultaneously. JS is single-threaded, but in cases where prerendering performance is network-bound (for example loading content from a remote CMS) this can speed things up by processing other tasks while waiting on the network response

  • 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

  • entries — 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] )

  • onError

    • 'fail' — (default) fails the build when a routing error is encountered when following a link

    • 'continue' — allows the build to continue, despite routing errors

    • function — custom error handler allowing you to log, throw and fail the build, or take other action of your choosing based on the details of the crawl

      import adapter from '@sveltejs/adapter-static';
      /** @type {import('@sveltejs/kit').PrerenderErrorHandler} */
      const handleError = ({ status, path, referrer, referenceType }) => {
      	if (path.startsWith('/blog')) throw new Error('Missing a blog page!');
      	console.warn(`${status} ${path}${referrer ? ` (${referenceType} from ${referrer})` : ''}`);
      export default {
      	kit: {
      		adapter: adapter(),
      		target: '#svelte',
      		prerender: {
      			onError: handleError


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


An object containing zero or more of the following values:

  • register - if set to false, will disable automatic service worker registration
  • files - a function with the type of (filepath: string) => boolean. When true, the given file will be available in $service-worker.files, otherwise it will be excluded.


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.


Whether to remove, append, or ignore trailing slashes when resolving URLs to routes.

  • "never" — redirect /x/ to /x
  • "always" — redirect /x to /x/
  • "ignore" — don't automatically add or remove trailing slashes. /x and /x/ will be treated equivalently

Ignoring trailing slashes is not recommended — the semantics of relative paths differ between the two cases (./y from /x is /y, but from /x/ is /x/y), and /x and /x/ are treated as separate URLs which is harmful to SEO. If you use this option, ensure that you implement logic for conditionally adding or removing trailing slashes from request.path inside your handle function.


A Vite config object, or a function that returns one. You can pass Vite and Rollup plugins via the plugins option to customize your build in advanced ways such as supporting image optimization, Tauri, WASM, Workbox, and more. SvelteKit will prevent you from setting certain build-related options since it depends on certain configuration values.

Additional Resources permalink


Please see the SvelteKit FAQ for solutions to common issues and helpful tips and tricks.

The Svelte FAQ and vite-plugin-svelte FAQ may also be helpful for questions deriving from those libraries.


We've written and published a few different SvelteKit sites as examples:

SvelteKit users have also published plenty of examples on GitHub, under the #sveltekit and #sveltekit-template topics, as well as on the Svelte Society site. Note that these have not been vetted by the maintainers and may not be up to date.


svelte-preprocess automatically transforms the code in your Svelte templates to provide support for TypeScript, PostCSS, scss/sass, Less, and many other technologies (except CoffeeScript which is not supported by SvelteKit). The first step of setting it up is to add svelte-preprocess to your svelte.config.js. It is provided by the template if you're using TypeScript whereas JavaScript users will need to add it. After that, you will often only need to install the corresponding library such as npm install -D sass or npm install -D less. See the svelte-preprocess docs for more details.

Svelte Adders allow you to setup many different complex integrations like Tailwind, PostCSS, Firebase, GraphQL, mdsvex, and more with a single command. Please see for a full listing of templates, components, and tools available for use with Svelte and SvelteKit.

The SvelteKit FAQ also has a section on integrations, which may be helpful if you run into any issues.


You can ask for help on Discord and StackOverflow. Please first search for information related to your issue in the FAQ, Google or another search engine, issue tracker, and Discord chat history in order to be respectful of others' time. There are many more people asking questions than answering them, so this will help in allowing the community to grow in a scalable fashion.

Appendix permalink

The core of SvelteKit provides a highly configurable rendering engine. This section describes some of the terms used when discussing rendering. A reference for setting these options is provided in the documentation above.


Server-side rendering (SSR) is the generation of the page contents on the server. SSR is generally preferred for SEO. While some search engines can index content that is dynamically generated on the client-side it may take longer even in these cases. It also tends to improve perceived performance and makes your app accessible to users if JavaScript fails or is disabled (which happens more often than you probably think).


Client-side rendering (CSR) is the generation of the page contents in the web browser using JavaScript. A single-page app (SPA) is an application in which all requests to the server load a single HTML file which then does client-side rendering of the requested contents based on the requested URL. All navigation is handled on the client-side in a process called client-side routing with per-page contents being updated and common layout elements remaining largely unchanged. SPAs do not provide SSR, which has the shortcoming described above. However, some applications are not greatly impacted by these shortcomings such as a complex business application behind a login where SEO would not be important and it is known that users will be accessing the application from a consistent computing environment.


Prerendering means computing the contents of a page at build time and saving the HTML for display. This approach has the same benefits as traditional server-rendered pages, but avoids recomputing the page for each visitor and so scales nearly for free as the number of visitors increases. The tradeoff is that the build process is more expensive and prerendered content can only be updated by building and deploying a new version of the application.

Not all pages can be prerendered. The basic rule is this: for content to be prerenderable, any two users hitting it directly must get the same content from the server. Note that you can still prerender content that is loaded based on the page's parameters as long as all users will be seeing the same prerendered content.

Pre-rendered pages are not limited to static content. You can build personalized pages if user-specific data is fetched and rendered client-side. This is subject to the caveat that you will experience the downsides of not doing SSR for that content as discussed above.


Static Site Generation (SSG) is a term that refers to a site where every page is prerendered. This is what SvelteKit's adapter-static does. SvelteKit was not built to do only static site generation like some tools and so may not scale as well to efficiently render a very large number of pages as tools built specifically for that purpose. However, in contrast to most purpose-built SSGs, SvelteKit does nicely allow for mixing and matching different rendering types on different pages. One benefit of fully prerendering a site is that you do not need to maintain or pay for servers to perform SSR. Once generated, the site can be served from CDNs, leading to great "time to first byte" performance. This delivery model is often referred to as JAMstack.


Svelte components store some state and update the DOM when the state is updated. When fetching data during SSR, by default SvelteKit will store this data and transmit it to the client along with the server-rendered HTML. The components can then be initialized on the client with that data without having to call the same API endpoints again. Svelte will then check that the DOM is in the expected state and attach event listeners in a process called hydration. Once the components are fully hydrated, they can react to changes to their properties just like any newly created Svelte component.


By default, when you navigate to a new page (by clicking on a link or using the browser's forward or back buttons), SvelteKit will intercept the attempted navigation and handle it instead of allowing the browser to send a request to the server for the destination page. SvelteKit will then update the displayed contents on the client by rendering the component for the new page, which in turn can make calls to the necessary API endpoints. This process of updating the page on the client in response to attempted navigation is called client-side routing.