Sveltekit En
Sveltekit En
1
sveltekit
Table of contents
Getting started - Introduction 4
Getting started - Creating a project 5
Getting started - Project structure 6
Getting started - Web standards 9
Core concepts - Routing 12
Core concepts - Load 20
Core concepts - Form actions 33
Core concepts - Page options 43
Core concepts - State management 49
Build and deploy - Building your app 53
Build and deploy - Adapters 54
Build and deploy - Adapter auto 55
Build and deploy - Adapter node 56
Build and deploy - Adapter static 61
Build and deploy - Single page apps 65
Build and deploy - Adapter cloudflare 67
Build and deploy - Adapter cloudflare workers 70
Build and deploy - Adapter netlify 73
Build and deploy - Adapter vercel 76
Build and deploy - Writing adapters 80
Advanced - Advanced routing 81
Advanced - Hooks 88
Advanced - Errors 94
Advanced - Link options 98
Advanced - Service workers 102
Advanced - Server only modules 105
Advanced - Assets 107
Advanced - Snapshots 108
Advanced - Packaging 109
Best practices - Accessibility 115
Best practices - Seo 118
Reference - Configuration 122
Reference - Cli 123
Reference - Modules 124
2
SVELTEKIT Docs - English
3
sveltekit
Introduction
Before we begin
If you're new to Svelte or SvelteKit we recommend checking out the interactive tutorial.
If you get stuck, reach out for help in the Discord chatroom.
What is SvelteKit?
SvelteKit is a framework for rapidly developing robust, performant web applications using Svelte. If you're
coming from React, SvelteKit is similar to Next. If you're coming from Vue, SvelteKit is similar to Nuxt.
To learn more about the kinds of applications you can build with SvelteKit, see the FAQ.
What is Svelte?
In short, Svelte is a way of writing user interface components — like a navigation bar, comment section, or
contact form — that users see and interact with in their browsers. The Svelte compiler converts your com‐
ponents to JavaScript that can be run to render the HTML for the page and to CSS that styles the page. You
don't need to know Svelte to understand the rest of this guide, but it will help. If you'd like to learn more,
check out the Svelte tutorial.
SvelteKit vs Svelte
Svelte renders UI components. You can compose these components and render an entire page with just
Svelte, but you need more than just Svelte to write an entire app.
SvelteKit provides basic functionality like a router — which updates the UI when a link is clicked — and
server-side rendering (SSR). But beyond that, building an app with all the modern best practices is fiendish‐
ly complicated. Those practices include build optimizations, so that you load only the minimal required code;
offline support; preloading pages before the user initiates navigation; configurable rendering that allows you
to render different parts of your app on the server with SSR, in the browser client-side rendering, or at
build-time with prerendering; and many other things. SvelteKit does all the boring stuff for you so that you
can get on with the creative part.
It reflects changes to your code in the browser instantly to provide a lightning-fast and feature-rich devel‐
opment experience by leveraging Vite with a Svelte plugin to do Hot Module Replacement (HMR).
Go to TOC
4
Creating a project
Creating a project
The easiest way to start building a SvelteKit app is to run npm create :
The first command will scaffold a new project in the my-app directory asking you if you'd like to set up
some basic tooling such as TypeScript. See integrations for pointers on setting up additional tooling. The
subsequent commands will then install its dependencies and start a server on localhost:5173.
Try editing the files to get a feel for how everything works.
Editor setup
We recommend using Visual Studio Code (aka VS Code) with the Svelte extension, but support also exists
for numerous other editors.
Go to TOC
5
sveltekit
Project structure
A typical SvelteKit project looks like this:
my-project/
├ src/
│ ├ lib/
│ │ ├ server/
│ │ │ └ [your server-only lib files]
│ │ └ [your lib files]
│ ├ params/
│ │ └ [your param matchers]
│ ├ routes/
│ │ └ [your routes]
│ ├ app.html
│ ├ error.html
│ ├ hooks.client.js
│ └ hooks.server.js
├ static/
│ └ [your static assets]
├ tests/
│ └ [your tests]
├ package.json
├ svelte.config.js
├ tsconfig.json
└ vite.config.js
You'll also find common files like .gitignore and .npmrc (and .prettierrc and .eslintrc.cjs and
so on, if you chose those options when running npm create svelte@latest ).
Project files
src
The src directory contains the meat of your project. Everything except src/routes and src/app.html
is optional.
lib contains your library code (utilities and components), which can be imported via the $lib alias, or
packaged up for distribution using svelte-package
server contains your server-only library code. It can be imported by using the $lib/server alias.
SvelteKit will prevent you from importing these in client code.
params contains any param matchers your app needs
routes contains the routes of your application. You can also colocate other components that are only
used within a single route here
app.html is your page template — an HTML document containing the following placeholders:
%sveltekit.head% — <link> and <script> elements needed by the app, plus any
<svelte:head> content
6
Project structure
%sveltekit.body% — the markup for a rendered page. This should live inside a <div> or other ele‐
ment, rather than directly inside <body> , to prevent bugs caused by browser extensions injecting el‐
ements that are then destroyed by the hydration process. SvelteKit will warn you in development if
this is not the case
%sveltekit.assets% — either paths.assets , if specified, or a relative path to paths.base
%sveltekit.nonce% — a CSP nonce for manually included links and scripts, if used
%sveltekit.env.[NAME]% - this will be replaced at render time with the [NAME] environment vari‐
able, which must begin with the publicPrefix (usually PUBLIC_ ). It will fallback to '' if not
matched.
error.html is the page that is rendered when everything else fails. It can contain the following
placeholders:
%sveltekit.status% — the HTTP status
%sveltekit.error.message% — the error message
hooks.client.js contains your client hooks
hooks.server.js contains your server hooks
service-worker.js contains your service worker
(Whether the project contains .js or .ts files depends on whether you opt to use TypeScript when you
create your project. You can switch between JavaScript and TypeScript in the documentation using the tog‐
gle at the bottom of this page.)
If you added Vitest when you set up your project, your unit tests will live in the src directory with a
.test.js extension.
static
Any static assets that should be served as-is, like robots.txt or favicon.png , go in here.
tests
If you added Playwright for browser testing when you set up your project, the tests will live in this directory.
package.json
Your package.json file must include @sveltejs/kit , svelte and vite as devDependencies .
When you create a project with npm create svelte@latest , you'll also notice that package.json in‐
cludes "type": "module" . This means that .js files are interpreted as native JavaScript modules with
import and export keywords. Legacy CommonJS files need a .cjs file extension.
svelte.config.js
This file contains your Svelte and SvelteKit configuration.
7
sveltekit
tsconfig.json
This file (or jsconfig.json , if you prefer type-checked .js files over .ts files) configures TypeScript, if
you added typechecking during npm create svelte@latest . Since SvelteKit relies on certain configuration
being set a specific way, it generates its own .svelte-kit/tsconfig.json file which your own config ex‐
tends .
vite.config.js
A SvelteKit project is really just a Vite project that uses the @sveltejs/kit/vite plugin, along with any
other Vite configuration.
Other files
.svelte-kit
As you develop and build your project, SvelteKit will generate files in a .svelte-kit directory (config‐
urable as outDir ). You can ignore its contents, and delete them at any time (they will be regenerated
when you next dev or build ).
Go to TOC
8
Web standards
Web standards
Throughout this documentation, you'll see references to the standard Web APIs that SvelteKit builds on top
of. Rather than reinventing the wheel, we use the platform, which means your existing web development
skills are applicable to SvelteKit. Conversely, time spent learning SvelteKit will help you be a better web de‐
veloper elsewhere.
These APIs are available in all modern browsers and in many non-browser environments like Cloudflare
Workers, Deno and Vercel Edge Functions. During development, and in adapters for Node-based environ‐
ments (including AWS Lambda), they're made available via polyfills where necessary (for now, that is —
Node is rapidly adding support for more web standards).
Fetch APIs
SvelteKit uses fetch for getting data from the network. It's available in hooks and server routes as well as
in the browser.
A special version of fetch is available in load functions, server hooks and API routes for invoking
endpoints directly during server-side rendering, without making an HTTP call, while preserving cre‐
dentials. (To make credentialled fetches in server-side code outside load , you must explicitly pass c
ookie and/or authorization headers.) It also allows you to make relative requests, whereas
server-side fetch normally requires a fully qualified URL.
Besides fetch itself, the Fetch API includes the following interfaces:
Request
An instance of Request is accessible in hooks and server routes as event.request . It contains useful
methods like request.json() and request.formData() for getting data that was posted to an endpoint.
Response
An instance of Response is returned from await fetch(...) and handlers in +server.js files.
Fundamentally, a SvelteKit app is a machine for turning a Request into a Response .
Headers
The Headers interface allows you to read incoming request.headers and set outgoing response.head‐
ers . For example, you can get the request.headers as shown below, and use the json convenience
function to send modified response.headers :
9
sveltekit
// @errors: 2461
/// file: src/routes/what-is-my-user-agent/+server.js
import { json } from '@sveltejs/kit';
FormData
When dealing with HTML native form submissions you'll be working with FormData objects.
// @errors: 2461
/// file: src/routes/hello/+server.js
import { json } from '@sveltejs/kit';
return json({
// get a specific field's value
name: body.get('name') ?? 'world'
});
}
Stream APIs
Most of the time, your endpoints will return complete data, as in the userAgent example above.
Sometimes, you may need to return a response that's too large to fit in memory in one go, or is delivered in
chunks, and for this the platform provides streams — ReadableStream, WritableStream and
TransformStream.
URL APIs
URLs are represented by the URL interface, which includes useful properties like origin and pathname
(and, in the browser, hash ). This interface shows up in various places — event.url in hooks and server
routes, $page.url in pages, from and to in beforeNavigate and afterNavigate and so on.
10
Web standards
URLSearchParams
Wherever you encounter a URL, you can access query parameters via url.searchParams , which is an in‐
stance of URLSearchParams :
// @filename: ambient.d.ts
declare global {
const url: URL;
}
export {};
// @filename: index.js
// cut---
const foo = url.searchParams.get('foo');
Web Crypto
The Web Crypto API is made available via the crypto global. It's used internally for Content Security Policy
headers, but you can also use it for things like generating UUIDs:
Go to TOC
11
sveltekit
Routing
At the heart of SvelteKit is a filesystem-based router. The routes of your app — i.e. the URL paths that
users can access — are defined by the directories in your codebase:
You can change src/routes to a different directory by editing the project config.
Each route directory contains one or more route files, which can be identified by their + prefix.
+page
+page.svelte
A +page.svelte component defines a page of your app. By default, pages are rendered both on the server
(SSR) for the initial request and in the browser (CSR) for subsequent navigation.
<h1>{data.title}</h1>
<div>{@html data.content}</div>
Note that SvelteKit uses <a> elements to navigate between routes, rather than a framework-specific
<Link> component.
12
Routing
+page.js
Often, a page will need to load some data before it can be rendered. For this, we add a +page.js module
that exports a load function:
This function runs alongside +page.svelte , which means it runs on the server during server-side render‐
ing and in the browser during client-side navigation. See load for full details of the API.
As well as load , +page.js can export values that configure the page's behaviour:
+page.server.js
If your load function can only run on the server — for example, if it needs to fetch data from a database
or you need to access private environment variables like API keys — then you can rename +page.js to
+page.server.js and change the PageLoad type to PageServerLoad .
// @filename: ambient.d.ts
declare global {
const getPostFromDatabase: (slug: string) => {
# string;
content: string;
}
}
export {};
// @filename: index.js
// cut---
import { error } from '@sveltejs/kit';
13
sveltekit
if (post) {
return post;
}
During client-side navigation, SvelteKit will load this data from the server, which means that the returned
value must be serializable using devalue. See load for full details of the API.
Like +page.js , +page.server.js can export page options — prerender , ssr and csr .
A +page.server.js file can also export actions. If load lets you read data from the server, actions let
you write data to the server using the <form> element. To learn how to use them, see the form actions
section.
+error
If an error occurs during load , SvelteKit will render a default error page. You can customise this error page
on a per-route basis by adding an +error.svelte file:
<h1>{$page.status}: {$page.error.message}</h1>
SvelteKit will 'walk up the tree' looking for the closest error boundary — if the file above didn't exist it would
try src/routes/blog/+error.svelte and then src/routes/+error.svelte before rendering the default
error page. If that fails (or if the error was thrown from the load function of the root +layout , which sits
'above' the root +error ), SvelteKit will bail out and render a static fallback error page, which you can cus‐
tomise by creating a src/error.html file.
If the error occurs inside a load function in +layout(.server).js , the closest error boundary in the tree
is an +error.svelte file above that layout (not next to it).
If no route can be found (404), src/routes/+error.svelte (or the default error page, if that file does not
exist) will be used.
+error.svelte is not used when an error occurs inside handle or a +server.js request handler.
14
Routing
+layout
So far, we've treated pages as entirely standalone components — upon navigation, the existing
+page.svelte 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.svelte , we can put them in layouts.
+layout.svelte
To create a layout that applies to every page, make a file called src/routes/+layout.svelte . The default
layout (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. The only requirement is that the com‐
ponent includes a <slot> for the page content. For example, let's add a nav bar:
<slot></slot>
...the nav will always be visible, and clicking between the three pages will only result in the <h1> being
replaced.
Layouts can be nested. 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 ex‐
ample, 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):
15
sveltekit
<h1>Settings</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
<slot></slot>
By default, each layout inherits the layout above it. Sometimes that isn't what you want - in this case, ad‐
vanced layouts can help you.
+layout.js
Just like +page.svelte loading data from +page.js , your +layout.svelte component can get data
from a load function in +layout.js .
If a +layout.js exports page options — prerender , ssr and csr — they will be used as defaults for
child pages.
Data returned from a layout's load function is also available to all its child pages:
Often, layout data is unchanged when navigating between pages. SvelteKit will intelligently rerun loa
d functions when necessary.
16
Routing
+layout.server.js
To run your layout's load function on the server, move it to +layout.server.js , and change the
LayoutLoad type to LayoutServerLoad .
Like +layout.js , +layout.server.js can export page options — prerender , ssr and csr .
+server
As well as pages, you can define routes with a +server.js file (sometimes referred to as an 'API route' or
an 'endpoint'), which gives you full control over the response. Your +server.js file exports functions cor‐
responding to HTTP verbs like GET , POST , PATCH , PUT , DELETE , OPTIONS , and HEAD that take a
RequestEvent argument and return a Response object.
if (isNaN(d) || d < 0) {
throw error(400, 'min and max must be numbers, and min must be less than
max');
}
The first argument to Response can be a ReadableStream , making it possible to stream large amounts of
data or create server-sent events (unless deploying to platforms that buffer responses, like AWS Lambda).
You can use the error , redirect and json methods from @sveltejs/kit for convenience (but you
don't have to).
If an error is thrown (either throw error(...) or an unexpected error), the response will be a JSON rep‐
resentation of the error or a fallback error page — which can be customised via src/error.html — de‐
pending on the Accept header. The +error.svelte component will not be rendered in this case. You can
read more about error handling here.
17
sveltekit
When creating an OPTIONS handler, note that Vite will inject Access-Control-Allow-Origin and A
ccess-Control-Allow-Methods headers — these will not be present in production unless you add
them.
Receiving data
By exporting POST / PUT / PATCH / DELETE / OPTIONS / HEAD handlers, +server.js files can be used to cre‐
ate a complete API:
<button on:click={add}>Calculate</button>
In general, form actions are a better way to submit data from the browser to the server.
Content negotiation
+server.js files can be placed in the same directory as +page files, allowing the same route to be either
a page or an API endpoint. To determine which, SvelteKit applies the following rules:
18
Routing
PUT / PATCH / DELETE / OPTIONS requests are always handled by +server.js since they do not apply to
pages
GET / POST / HEAD requests are treated as page requests if the accept header prioritises text/html
(in other words, it's a browser page request), else they are handled by +server.js .
Responses to GET requests will include a Vary: Accept header, so that proxies and browsers cache
HTML and JSON responses separately.
$types
Throughout the examples above, we've been importing types from a $types.d.ts file. This is a file
SvelteKit creates for you in a hidden directory if you're using TypeScript (or JavaScript with JSDoc type an‐
notations) to give you type safety when working with your root files.
For example, annotating export let data with PageData (or LayoutData , for a +layout.svelte file)
tells TypeScript that the type of data is whatever was returned from load :
If you're using VS Code or any IDE that supports the language server protocol and TypeScript plugins then
you can omit these types entirely! Svelte's IDE tooling will insert the correct types for you, so you'll get type
checking without writing them yourself. It also works with our command line tool svelte-check .
You can read more about omitting $types in our blog post about it.
Other files
Any other files inside a route directory are ignored by SvelteKit. This means you can colocate components
and utility modules with the routes that need them.
If components and modules are needed by multiple routes, it's a good idea to put them in $lib .
Further reading
Tutorial: Routing
Tutorial: API routes
Docs: Advanced routing
Go to TOC
19
sveltekit
Loading data
Before a +page.svelte component (and its containing +layout.svelte components) can be rendered,
we often need to get some data. This is done by defining load functions.
Page data
A +page.svelte file can have a sibling +page.js that exports a load function, the return value of which
is available to the page via the data prop:
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
A load function in a +page.js file runs both on the server and in the browser (unless combined with ex‐
port const ssr = false , in which case it will only run in the browser). If your load function should al‐
ways run on the server (because it uses private environment variables, for example, or accesses a data‐
base) then it would go in a +page.server.js instead.
A more realistic version of your blog post's load function, that only runs on the server and pulls data from
a database, might look like this:
// @filename: index.js
// cut---
import * as db from '$lib/server/database';
20
Loading data
return {
post: await db.getPost(params.slug)
};
}
Notice that the type changed from PageLoad to PageServerLoad , because server load functions can ac‐
cess additional arguments. To understand when to use +page.js and when to use +page.server.js , see
Universal vs server.
Layout data
Your +layout.svelte files can also load data, via +layout.js or +layout.server.js .
// @filename: index.js
// cut---
import * as db from '$lib/server/database';
<main>
<!-- +page.svelte is rendered in this <slot> -->
<slot />
</main>
<aside>
<h2>More posts</h2>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">
{post.title}
</a>
</li>
{/each}
</ul>
</aside>
Data returned from layout load functions is available to child +layout.svelte components and the
+page.svelte component as well as the layout that it 'belongs' to.
21
sveltekit
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
+{#if next}
+ <p>Next post: <a href="/blog/{next.slug}">{next.title}</a></p>
+{/if}
If multiple load functions return data with the same key, the last one 'wins' — the result of a layout
load returning { a: 1, b: 2 } and a page load returning { b: 3, c: 4 } would be { a: 1,
b: 3, c: 4 } .
$page.data
The +page.svelte component, and each +layout.svelte component above it, has access to its own
data plus all the data from its parents.
In some cases, we might need the opposite — a parent layout might need to access page data or data from
a child layout. For example, the root layout might want to access a title property returned from a load
function in +page.js or +page.server.js . This can be done with $page.data :
<svelte:head>
<title>{$page.data.title}</title>
</svelte:head>
Universal vs server
As we've seen, there are two types of load function:
+page.js and +layout.js files export universal load functions that run both on the server and in the
browser
22
Loading data
+page.server.js and +layout.server.js files export server load functions that only run server-
side
Conceptually, they're the same thing, but there are some important differences to be aware of.
By default, universal load functions run on the server during SSR when the user first visits your page.
They will then run again during hydration, reusing any responses from fetch requests. All subsequent invo‐
cations of universal load functions happen in the browser. You can customize the behavior through page
options. If you disable server side rendering, you'll get an SPA and universal load functions always run on
the client.
A load function is invoked at runtime, unless you prerender the page — in that case, it's invoked at build
time.
Input
Both universal and server load functions have access to properties describing the request ( params ,
route and url ) and various functions ( fetch , setHeaders , parent and depends ). These are de‐
scribed in the following sections.
Server load functions are called with a ServerLoadEvent , which inherits clientAddress , cookies ,
locals , platform and request from RequestEvent .
Universal load functions are called with a LoadEvent , which has a data property. If you have load
functions in both +page.js and +page.server.js (or +layout.js and +layout.server.js ), the return
value of the server load function is the data property of the universal load function's argument.
Output
A universal load function can return an object containing any values, including things like custom classes
and component constructors.
A server load function must return data that can be serialized with devalue — anything that can be repre‐
sented as JSON plus things like BigInt , Date , Map , Set and RegExp , or repeated/cyclical references —
so that it can be transported over the network. Your data can include promises, in which case it will be
streamed to browsers.
23
sveltekit
Universal load functions are useful when you need to fetch data from an external API and don't need
private credentials, since SvelteKit can get the data directly from the API rather than going via your server.
They are also useful when you need to return something that can't be serialized, such as a Svelte compo‐
nent constructor.
In rare cases, you might need to use both together — for example, you might need to return an instance of
a custom class that was initialised with data from your server.
url
An instance of URL , containing properties like the origin , hostname , pathname and searchParams
(which contains the parsed query string as a URLSearchParams object). url.hash cannot be accessed
during load , since it is unavailable on the server.
In some environments this is derived from request headers during server-side rendering. If you're us‐
ing adapter-node, for example, you may need to configure the adapter in order for the URL to be
correct.
route
Contains the name of the current route directory, relative to src/routes :
params
params is derived from url.pathname and route.id .
Given a route.id of /a/[b]/[...c] and a url.pathname of /a/x/y/z , the params object would look
like this:
{
"b": "x",
"c": "y/z"
}
24
Loading data
It can be used to make credentialed requests on the server, as it inherits the cookie and authoriza‐
tion headers for the page request.
It can make relative requests on the server (ordinarily, fetch requires a URL with an origin when used
in a server context).
Internal requests (e.g. for +server.js routes) go directly to the handler function when running on the
server, without the overhead of an HTTP call.
During server-side rendering, the response will be captured and inlined into the rendered HTML by hook‐
ing into the text and json methods of the Response object. Note that headers will not be serialized,
unless explicitly included via filterSerializedResponseHeaders .
During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an
additional network request - if you received a warning in your browser console when using the browser
fetch instead of the load fetch , this is why.
return { item };
}
Cookies
A server load function can get and set cookies .
// @filename: index.js
// cut---
import * as db from '$lib/server/database';
return {
user: await db.getUser(sessionid)
};
}
25
sveltekit
Cookies will only be passed through the provided fetch function if the target host is the same as the
SvelteKit application or a more specific subdomain of it.
Other cookies will not be passed when credentials: 'include' is set, because SvelteKit does not know
which domain which cookie belongs to (the browser does not pass this information along), so it's not safe to
forward any of them. Use the handleFetch hook to work around it.
When setting cookies, be aware of the path property. By default, the path of a cookie is the current
pathname. If you for example set a cookie at page admin/user , the cookie will only be available
within the admin pages by default. In most cases you likely want to set path to '/' to make the
cookie available throughout your app.
Headers
Both server and universal load functions have access to a setHeaders function that, when running on the
server, can set headers for the response. (When running in the browser, setHeaders has no effect.) This is
useful if you want the page to be cached, for example:
return response.json();
}
Setting the same header multiple times (even in separate load functions) is an error — you can only set a
given header once. You cannot add a set-cookie header with setHeaders — use cookies.set(name,
value, options) instead.
26
Loading data
Notice that the load function in +page.js receives the merged data from both layout load func‐
tions, not just the immediate parent.
Inside +page.server.js and +layout.server.js , parent returns data from parent +layout.serv‐
er.js files.
In +page.js or +layout.js it will return data from parent +layout.js files. However, a missing +lay‐
out.js is treated as a ({ data }) => data function, meaning that it will also return data from parent
+layout.server.js files that are not 'shadowed' by a +layout.js file
Take care not to introduce waterfalls when using await parent() . Here, for example, getData(params)
does not depend on the result of calling parent() , so we should call it first to avoid a delayed render.
27
sveltekit
return {
...data
meta: { ...parentData.meta, ...data.meta }
};
}
Errors
If an error is thrown during load , the nearest +error.svelte will be rendered. For expected errors, use
the error helper from @sveltejs/kit to specify the HTTP status code and an optional message:
// @filename: index.js
// cut---
import { error } from '@sveltejs/kit';
if (!locals.user.isAdmin) {
throw error(403, 'not an admin');
}
}
If an unexpected error is thrown, SvelteKit will invoke handleError and treat it as a 500 Internal Error.
Redirects
To redirect users, use the redirect helper from @sveltejs/kit to specify the location to which they
should be redirected alongside a 3xx status code.
// @filename: index.js
28
Loading data
// cut---
import { redirect } from '@sveltejs/kit';
Make sure you're not catching the thrown redirect, which would prevent SvelteKit from handling it.
In the browser, you can also navigate programmatically outside of a load function using goto from $ap‐
p.navigation .
<p>
one: {data.one}
</p>
<p>
two: {data.two}
</p>
<p>
three:
29
sveltekit
{#await data.streamed.three}
Loading...
{:then value}
{value}
{:catch error}
{error.message}
{/await}
</p>
On platforms that do not support streaming, such as AWS Lambda, responses will be buffered. This
means the page will only render once all promises resolve. If you are using a proxy (e.g. NGINX),
make sure it does not buffer responses from the proxied server.
Streaming data will only work when JavaScript is enabled. You should avoid returning nested promises
from a universal load function if the page is server rendered, as these are not streamed — instead,
the promise is recreated when the function reruns in the browser.
The headers and status code of a response cannot be changed once the response has started stream‐
ing, therefore you cannot setHeaders or throw redirects inside a streamed promise.
Parallel loading
When rendering (or navigating to) a page, SvelteKit runs all load functions concurrently, avoiding a water‐
fall of requests. During client-side navigation, the result of calling multiple server load functions are
grouped into a single response. Once all load functions have returned, the page is rendered.
// @filename: index.js
// cut---
import * as db from '$lib/server/database';
30
Loading data
return {
post: await db.getPost(params.slug)
};
}
// @filename: index.js
// cut---
import * as db from '$lib/server/database';
A load function that calls await parent() will also rerun if a parent load function is rerun.
Dependency tracking does not apply after the load function has returned — for example, accessing
params.x inside a nested promise will not cause the function to rerun when params.x changes. (Don't
worry, you'll get a warning in development if you accidentally do this.) Instead, access the parameter in the
main body of your load function.
Manual invalidation
You can also rerun load functions that apply to the current page using invalidate(url) , which reruns all
load functions that depend on url , and invalidateAll() , which reruns every load function. Server
load functions will never automatically depend on a fetched url to avoid leaking secrets to the client.
A load function depends on url if it calls fetch(url) or depends(url) . Note that url can be a cus‐
tom identifier that starts with [a-z]: :
return {
31
sveltekit
function rerunLoadFunction() {
// any of these will cause the `load` function to rerun
invalidate('app:random');
invalidate('https://api.example.com/random-number');
invalidate(url => url.href.includes('random-number'));
invalidateAll();
}
</script>
params and url can change in response to a <a href=".."> link click, a <form> interaction, a goto
invocation, or a redirect .
Note that rerunning a load function will update the data prop inside the corresponding +layout.svelte
or +page.svelte ; it does not cause the component to be recreated. As a result, internal state is pre‐
served. If this isn't what you want, you can reset whatever you need to reset inside an afterNavigate
callback, and/or wrap your component in a {#key ...} block.
Further reading
Tutorial: Loading data
Tutorial: Errors and redirects
Tutorial: Advanced loading
Go to TOC
32
Form actions
Form actions
A +page.server.js file can export actions, which allow you to POST data to the server using the <form>
element.
When using <form> , client-side JavaScript is optional, but you can easily progressively enhance your form
interactions with JavaScript to provide the best user experience.
Default actions
In the simplest case, a page declares a default action:
To invoke this action from the /login page, just add a <form> — no JavaScript needed:
If someone were to click the button, the browser would send the form data via POST request to the server,
running the default action.
Actions always use POST requests, since GET requests should never have side-effects.
We can also invoke the action from other pages (for example if there's a login widget in the nav in the root
layout) by adding the action attribute, pointing to the page:
33
sveltekit
Named actions
Instead of one default action, a page can have as many named actions as it needs:
To invoke a named action, add a query parameter with the name prefixed by a / character:
As well as the action attribute, we can use the formaction attribute on a button to POST the same form
data to a different action than the parent <form> :
We can't have default actions next to named actions, because if you POST to a named action without
a redirect, the query parameter is persisted in the URL, which means the next default POST would go
through the named action from before.
34
Form actions
Anatomy of an action
Each action receives a RequestEvent object, allowing you to read the data with request.formData() .
After processing the request (for example, logging the user in by setting a cookie), the action can respond
with data that will be available through the form property on the corresponding page and through
$page.form app-wide until the next update.
// @errors: 2304
/// file: src/routes/login/+page.server.js
/** @type {import('./$types').PageServerLoad} */
export async function load({ cookies }) {
const user = await db.getUserFromSession(cookies.get('sessionid'));
return { user };
}
{#if form?.success}
<!-- this message is ephemeral; it exists because the page was rendered in
response to a form submission. it will vanish if the user reloads -->
<p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}
Validation errors
If the request couldn't be processed because of invalid data, you can return validation errors — along with
the previously submitted form values — back to the user so that they can try again. The fail function lets
you return an HTTP status code (typically 400 or 422, in the case of validation errors) along with the data.
The status code is available through $page.status and the data through form :
35
sveltekit
+ if (!email) {
+ return fail(400, { email, missing: true });
+ }
Note that as a precaution, we only return the email back to the page — not the password.
The returned data must be serializable as JSON. Beyond that, the structure is entirely up to you. For exam‐
ple, if you had multiple forms on the page, you could distinguish which <form> the returned form data re‐
ferred to with an id property or similar.
Redirects
Redirects (and errors) work exactly the same as in load :
36
Form actions
+ if (url.searchParams.has('redirectTo')) {
+ throw redirect(303, url.searchParams.get('redirectTo'));
+ }
Loading data
After an action runs, the page will be re-rendered (unless a redirect or an unexpected error occurs), with
the action's return value available to the page as the form prop. This means that your page's load func‐
tions will run after the action completes.
Note that handle runs before the action is invoked, and does not rerun before the load functions. This
means that if, for example, you use handle to populate event.locals based on a cookie, you must up‐
date event.locals when you set or delete the cookie in an action:
// @filename: global.d.ts
declare global {
function getUser(sessionid: string | undefined): {
name: string;
};
}
37
sveltekit
export {};
// @filename: index.js
// cut---
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
event.locals.user = await getUser(event.cookies.get('sessionid'));
return resolve(event);
}
// @filename: index.js
// cut---
/** @type {import('./$types').PageServerLoad} */
export function load(event) {
return {
user: event.locals.user
};
}
Progressive enhancement
In the preceding sections we built a /login action that works without client-side JavaScript — not a
fetch in sight. That's great, but when JavaScript is available we can progressively enhance our form inter‐
actions to provide a better user experience.
use:enhance
The easiest way to progressively enhance a form is to add the use:enhance action:
38
Form actions
Yes, it's a little confusing that the enhance action and <form action> are both called 'action'. These
docs are action-packed. Sorry.
Without an argument, use:enhance will emulate the browser-native behaviour, just without the full-page
reloads. It will:
update the form property, $page.form and $page.status on a successful or invalid response, but
only if the action is on the same page you're submitting from. So for example if your form looks like
<form action="/somewhere/else" ..> , form and $page will not be updated. This is because in the
native form submission case you would be redirected to the page the action is on. If you want to have
them updated either way, use applyAction
reset the <form> element and invalidate all data using invalidateAll on a successful response
call goto on a redirect response
render the nearest +error boundary if an error occurs
reset focus to the appropriate element
To customise the behaviour, you can provide a SubmitFunction that runs immediately before the form is
submitted, and (optionally) returns a callback that runs with the ActionResult . Note that if you return a
callback, the default behavior mentioned above is not triggered. To get it back, call update .
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel, submitter }) => {
// `formElement` is this `<form>` element
// `formData` is its `FormData` object that's about to be submitted
// `action` is the URL to which the form is posted
// calling `cancel()` will prevent the submission
// `submitter` is the `HTMLElement` that caused the form to be submitted
You can use these functions to show and hide loading UI, and so on.
applyAction
If you provide your own callbacks, you may need to reproduce part of the default use:enhance behaviour,
such as showing the nearest +error boundary. Most of the time, calling update passed to the callback is
enough. If you need more customization you can do so with applyAction :
39
sveltekit
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel }) => {
success , failure — sets $page.status to result.status and updates form and $page.form to
result.data (regardless of where you are submitting from, in contrast to update from enhance )
redirect — calls goto(result.location)
error — renders the nearest +error boundary with result.error
40
Form actions
applyAction(result);
}
</script>
Note that you need to deserialize the response before processing it further using the corresponding
method from $app/forms . JSON.parse() isn't enough because form actions - like load functions - also
support returning Date or BigInt objects.
If you have a +server.js alongside your +page.server.js , fetch requests will be routed there by de‐
fault. To POST to an action in +page.server.js instead, use the custom x-sveltekit-action header:
Alternatives
Form actions are the preferred way to send data to the server, since they can be progressively enhanced,
but you can also use +server.js files to expose (for example) a JSON API. Here's how such an interaction
could look like:
GET vs POST
As we've seen, to invoke a form action you must use method="POST" .
41
sveltekit
Some forms don't need to POST data to the server — search inputs, for example. For these you can use
method="GET" (or, equivalently, no method at all), and SvelteKit will treat them like <a> elements, using
the client-side router instead of a full page navigation:
<form action="/search">
<label>
Search
<input name="q">
</label>
</form>
Submitting this form will navigate to /search?q=... and invoke your load function but will not invoke an
action. As with <a> elements, you can set the data-sveltekit-reload , data-sveltekit-replaces‐
tate , data-sveltekit-keepfocus and data-sveltekit-noscroll attributes on the <form> to control
the router's behaviour.
Further reading
Tutorial: Forms
Go to TOC
42
Page options
Page options
By default, SvelteKit will render (or prerender) 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
initialize a router that takes over subsequent navigations.
You can control each of these on a page-by-page basis by exporting options from +page.js or
+page.server.js , or for groups of pages using a shared +layout.js or +layout.server.js . To define
an option for the whole app, export it from the root layout. Child layouts and pages override values set in
parent layouts, so — for example — you can enable prerendering for your entire app then disable it for
pages that need to be dynamically rendered.
You can mix and match these options in different areas of your app. For example you could prerender your
marketing page for maximum speed, server-render your dynamic pages for SEO and accessibility and turn
your admin section into an SPA by rendering it on the client only. This makes SvelteKit very versatile.
prerender
It's likely that at least some routes of your app can be represented as a simple HTML file generated at build
time. These routes can be prerendered.
Alternatively, you can set export const prerender = true in your root +layout.js or +layout.serv‐
er.js and prerender everything except pages that are explicitly marked as not prerenderable:
Routes with prerender = true will be excluded from manifests used for dynamic SSR, making your server
(or serverless/edge functions) smaller. In some cases you might want to prerender a route but also include
it in the manifest (for example, with a route like /blog/[slug] where you want to prerender your most re‐
cent/popular content but server-render the long tail) — for these cases, there's a third option, 'auto':
If your entire app is suitable for prerendering, you can use adapter-static , which will output files
suitable for use with any static webserver.
43
sveltekit
The prerenderer will start at the root of your app and generate files for any prerenderable pages or +serv‐
er.js routes 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 config.k‐
it.prerender.entries , or by exporting an entries function from your dynamic route.
While prerendering, the value of building imported from $app/environment will be true .
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, such as a
src/routes/blog/[slug]/+page.svelte route.
Accessing url.searchParams during prerendering is forbidden. If you need to use it, ensure you are only
doing so in the browser (for example in onMount ).
Pages with actions cannot be prerendered, because a server must be able to handle the action POST
requests.
44
Page options
Route conflicts
Because prerendering writes to the filesystem, it isn't possible to have two endpoints that would cause a di‐
rectory and a file to have the same name. For example, src/routes/foo/+server.js and
src/routes/foo/bar/+server.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.json/+server.js and src/routes/foo/bar.json/+server.js would result in foo.j‐
son 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 .
Troubleshooting
If you encounter an error like 'The following routes were marked as prerenderable, but were not preren‐
dered' it's because the route in question (or a parent layout, if it's a page) has export const prerender =
true but the page wasn't actually prerendered, because it wasn't reached by the prerendering crawler.
Since these routes cannot be dynamically server-rendered, this will cause errors when people try to access
the route in question. There are two ways to fix it:
Ensure that SvelteKit can find the route by following links from config.kit.prerender.entries or the
entries page option. Add links to dynamic routes (i.e. pages with [parameters] ) to this option if
they are not found through crawling the other entry points, else they are not prerendered because
SvelteKit doesn't know what value the parameters should have. Pages not marked as prerenderable will
be ignored and their links to other pages will not be crawled, even if some of them would be
prerenderable.
Change export const prerender = true to export const prerender = 'auto' . Routes with
'auto' can be dynamically server rendered
entries
SvelteKit will discover pages to prerender automatically, by starting at entry points and crawling them. By
default, all your non-dynamic routes are considered entry points — for example, if you have these routes...
/ # non-dynamic
/blog # non-dynamic
/blog/[slug] # dynamic, because of `[slug]`
45
sveltekit
...SvelteKit will prerender / and /blog , and in the process discover links like <a href="/blog/hello-
world"> which give it new pages to prerender.
Most of the time, that's enough. In some situations, links to pages like /blog/hello-world might not exist
(or might not exist on prerendered pages), in which case we need to tell SvelteKit about their existence.
entries can be an async function, allowing you to (for example) retrieve a list of posts from a CMS or
database, in the example above.
ssr
Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it's hy‐
drated. If you set ssr to false , it renders an empty 'shell' page instead. This is useful if your page is un‐
able to be rendered on the server (because you use browser-only globals like document for example), but
in most situations it's not recommended (see appendix).
If you add export const ssr = false to your root +layout.js , your entire app will only be rendered on
the client — which essentially means you turn your app into an SPA.
csr
Ordinarily, SvelteKit hydrates your server-rendered HTML into an interactive client-side-rendered (CSR)
page. Some pages don't require JavaScript at all — many blog posts and 'about' pages fall into this catego‐
ry. In these cases you can disable CSR:
46
Page options
trailingSlash
By default, SvelteKit will remove trailing slashes from URLs — if you visit /about/ , it will respond with a
redirect to /about . You can change this behaviour with the trailingSlash option, which can be one of
'never' (the default), 'always' , or 'ignore' .
As with other page options, you can export this value from a +layout.js or a +layout.server.js and it
will apply to all child pages. You can also export the configuration from +server.js files.
This option also affects prerendering. If trailingSlash is always , a route like /about will result in an
about/index.html file, otherwise it will create about.html , mirroring static webserver conventions.
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.
config
With the concept of adapters, SvelteKit is able to run on a variety of platforms. Each of these might have
specific configuration to further tweak the deployment — for example on Vercel you could choose to deploy
some parts of your app on the edge and others on serverless environments.
config is an object with key-value pairs at the top level. Beyond that, the concrete shape is dependent on
the adapter you're using. Every adapter should provide a Config interface to import for type safety.
Consult the documentation of your adapter for more information.
// @filename: ambient.d.ts
declare module 'some-adapter' {
export interface Config { runtime: string }
}
// @filename: index.js
// cut---
/// file: src/routes/+page.js
/** @type {import('some-adapter').Config} */
export const config = {
runtime: 'edge'
};
config objects are merged at the top level (but not deeper levels). This means you don't need to repeat
all the values in a +page.js if you want to only override some of the values in the upper +layout.js . For
example this layout configuration...
47
sveltekit
...which results in the config value { runtime: 'edge', regions: ['us1', 'us2'], foo: { baz: true
} } for that page.
Further reading
Tutorial: Page options
Go to TOC
48
State management
State management
If you're used to building client-only apps, state management in an app that spans server and client might
seem intimidating. This section provides tips for avoiding some common gotchas.
Conceptually, that is. In reality, servers are often long-lived and shared by multiple users. For that reason
it's important not to store data in shared variables. For example, consider this code:
// NEVER DO THIS!
user = {
name: data.get('name'),
embarrassingSecret: data.get('secret')
};
}
}
The user variable is shared by everyone who connects to this server. If Alice submitted an embarrassing
secret, and Bob visited the page after her, Bob would know Alice's secret. In addition, when Alice returns to
the site later in the day, the server may have restarted, losing her data.
Instead, you should authenticate the user using cookies and persist the data to a database.
No side-effects in load
For the same reason, your load functions should be pure — no side-effects (except maybe the occasional
console.log(...) ). For example, you might be tempted to write to a store inside a load function so that
you can use the store value in your components:
49
sveltekit
// @filename: index.js
// cut---
import { user } from '$lib/user';
// NEVER DO THIS!
user.set(await response.json());
}
As with the previous example, this puts one user's information in a place that is shared by all users.
Instead, just return the data...
+ return {
+ user: await response.json()
+ };
}
...and pass it around to the components that need it, or use $page.data .
If you're not using SSR, then there's no risk of accidentally exposing one user's data to another. But you
should still avoid side-effects in your load functions — your application will be much easier to reason about
without them.
50
State management
<p>Welcome {$user.name}</p>
Updating the context-based store value in deeper-level pages or components will not affect the value in the
parent component when the page is rendered via SSR: The parent component has already been rendered
by the time the store value is updated. To avoid values 'flashing' during state updates during hydration, it is
generally recommended to pass state down into components rather than up.
If you're not using SSR (and can guarantee that you won't need to use SSR in future) then you can safely
keep state in a shared module, without using the context API.
<header>
<h1>{data.title}</h1>
<p>Reading time: {Math.round(estimatedReadingTime)} minutes</p>
</header>
<div>{@html data.content}</div>
51
sveltekit
Reusing components like this means that things like sidebar scroll state are preserved, and you can easily
animate between changing values. However, if you do need to completely destroy and remount a compo‐
nent on navigation, you can use this pattern:
{#key $page.url.pathname}
<BlogPost title={data.title} content={data.title} />
{/key}
Go to TOC
52
Building your app
Firstly, Vite creates an optimized production build of your server code, your browser code, and your service
worker (if you have one). Prerendering is executed at this stage, if appropriate.
Secondly, an adapter takes this production build and tunes it for your target environment — more on this on
the following pages.
+if (!building) {
setupMyDatabase();
+}
Go to TOC
53
sveltekit
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 for deployment.
Official adapters exist for a variety of platforms — these are documented on the following pages:
Using adapters
Your adapter is specified in svelte.config.js :
// @filename: index.js
// cut---
import adapter from 'svelte-adapter-foo';
Platform-specific context
Some adapters may have access to additional information about the request. For example, Cloudflare
Workers can access an env object containing KV namespaces etc. This can be passed to the
RequestEvent used in hooks and server routes as the platform property — consult each adapter's docu‐
mentation to learn more.
Go to TOC
54
Zero-config deployments
Zero-config deployments
When you create a new SvelteKit project with npm create svelte@latest , it installs adapter-auto by
default. This adapter automatically installs and uses the correct adapter for supported environments when
you deploy:
It's recommended to install the appropriate adapter to your devDependencies once you've settled on a
target environment, since this will add the adapter to your lockfile and slightly improve install times on CI.
Environment-specific configuration
To add configuration options, such as { edge: true } in adapter-vercel and adapter-netlify , you
must install the underlying adapter — adapter-auto does not take any options.
Go to TOC
55
sveltekit
Node servers
To generate a standalone Node server, use adapter-node .
Usage
Install with npm i -D @sveltejs/adapter-node , then add the adapter to your svelte.config.js :
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter()
}
};
Deploying
First, build your app with npm run build . This will create the production server in the output directory
specified in the adapter options, defaulting to build .
You will need the output directory, the project's package.json , and the production dependencies in
node_modules to run the application. Production dependencies can be generated by copying the pack‐
age.json and package-lock.json and then running npm ci --omit dev (you can skip this step if your
app doesn't have any dependencies). You can then start your app with this command:
node build
Development dependencies will be bundled into your app using Rollup. To control whether a given package
is bundled or externalised, place it in devDependencies or dependencies respectively in your pack‐
age.json .
Environment variables
In dev and preview , SvelteKit will read environment variables from your .env file (or .env.local , or
.env.[mode] , as determined by Vite.)
In production, .env files are not automatically loaded. To do so, install dotenv in your project...
-node build
+node -r dotenv/config build
56
Node servers
Alternatively, the server can be configured to accept connections on a specified socket path. When this is
done using the SOCKET_PATH environment variable, the HOST and PORT environment variables will be
disregarded.
With this, a request for the /stuff pathname will correctly resolve to https://my.site/stuff .
Alternatively, you can specify headers that tell SvelteKit about the request protocol and host, from which it
can construct the origin URL:
x-forwarded-proto and x-forwarded-host are de facto standard headers that forward the original
protocol and host if you're using a reverse proxy (think load balancers and CDNs). You should only set
these variables if your server is behind a trusted reverse proxy; otherwise, it'd be possible for clients
to spoof these headers.
If adapter-node can't correctly determine the URL of your deployment, you may experience this error
when using form actions:
57
sveltekit
Headers can easily be spoofed. As with PROTOCOL_HEADER and HOST_HEADER , you should know what
you're doing before setting these.
If the ADDRESS_HEADER is X-Forwarded-For , the header value will contain a comma-separated list of IP
addresses. The XFF_DEPTH environment variable should specify how many trusted proxies sit in front of
your server. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the original con‐
nection and the first two proxies:
Some guides will tell you to read the left-most address, but this leaves you vulnerable to spoofing:
We instead read from the right, accounting for the number of trusted proxies. In this case, we would use
XFF_DEPTH=3 .
If you need to read the left-most address instead (and don't care about spoofing) — for example, to
offer a geolocation service, where it's more important for the IP address to be real than trusted, you
can do so by inspecting the x-forwarded-for header within your app.
BODY_SIZE_LIMIT
The maximum request body size to accept in bytes including while streaming. Defaults to 512kb. You can
disable this option with a value of 0 and implement a custom check in handle if you need something more
advanced.
Options
The adapter can be configured with various options:
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-node';
58
Node servers
export default {
kit: {
adapter: adapter({
// default options are shown
out: 'build',
precompress: false,
envPrefix: '',
polyfill: true
})
}
};
out
The directory to build the server to. It defaults to build — i.e. node build would start the server locally
after it has been created.
precompress
Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to false .
envPrefix
If you need to change the name of the environment variables used to configure the deployment (for exam‐
ple, to deconflict with environment variables you don't control), you can specify a prefix:
envPrefix: 'MY_CUSTOM_';
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build
polyfill
Controls whether your build will load polyfills for missing modules. It defaults to true , and should only be
disabled when using Node 18.11 or greater.
Note: to use Node's built-in crypto global with Node 18 you will need to use the --experimental-glob‐
al-webcrypto flag. This flag is not required with Node 20.
Custom server
The adapter creates two files in your build directory — index.js and handler.js . Running index.js —
e.g. node build , if you use the default build directory — will start a server on the configured port.
Alternatively, you can import the handler.js file, which exports a handler suitable for use with Express,
Connect or Polka (or even just the built-in http.createServer ) and set up your own server:
59
sveltekit
// let SvelteKit handle everything else, including serving prerendered pages and
static assets
app.use(handler);
app.listen(3000, () => {
console.log('listening on port 3000');
});
Troubleshooting
Is there a hook for cleaning up before the server
exits?
There's nothing built-in to SvelteKit for this, because such a cleanup hook depends highly on the execution
environment you're on. For Node, you can use its built-in process.on(..) to implement a callback that
runs before the server exits:
process.on('SIGINT', shutdownGracefully);
process.on('SIGTERM', shutdownGracefully);
Go to TOC
60
Static site generation
This will prerender your entire site as a collection of static files. If you'd like to prerender only some pages
and dynamically server-render others, you will need to use a different adapter together with the preren‐
der option.
Usage
Install with npm i -D @sveltejs/adapter-static , then add the adapter to your svelte.config.js :
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
// default options are shown. On some platforms
// these options are set automatically — see below
pages: 'build',
assets: 'build',
fallback: undefined,
precompress: false,
strict: true
})
}
};
You must ensure SvelteKit's trailingSlash option is set appropriately for your environment. If your
host does not render /a.html upon receiving a request for /a then you will need to set trailingSl
ash: 'always' in your root layout to create /a/index.html instead.
Zero-config support
Some platforms have zero-config support (more to come in future):
Vercel
On these platforms, you should omit the adapter options so that adapter-static can provide the optimal
configuration:
61
sveltekit
Options
pages
The directory to write prerendered pages to. It defaults to build .
assets
The directory to write static assets (the contents of static , plus client-side JS and CSS generated by
SvelteKit) to. Ordinarily this should be the same as pages , and it will default to whatever the value of
pages is, but in rare circumstances you might need to output pages and assets to separate locations.
fallback
Specify a fallback page for SPA mode, e.g. index.html or 200.html or 404.html .
precompress
If true , precompresses files with brotli and gzip. This will generate .br and .gz files.
strict
By default, adapter-static checks that either all pages and endpoints (if any) of your app were preren‐
dered, or you have the fallback option set. This check exists to prevent you from accidentally publishing
an app where some parts of it are not accessible, because they are not contained in the final output. If you
know this is ok (for example when a certain page only exists conditionally), you can set strict to false
to turn off this check.
GitHub Pages
When building for GitHub Pages, make sure to update paths.base to match your repo name, since the site
will be served from https://your-username.github.io/your-repo-name rather than from the root.
You will have to prevent GitHub's provided Jekyll from managing your site by putting an empty .nojekyll
file in your static folder.
62
Static site generation
You can use GitHub actions to automatically deploy your site to GitHub Pages when you make a change.
Here's an example workflow:
on:
push:
branches: 'main'
jobs:
build_site:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# If you're using pnpm, add this step then change the commands and cache key
below to use `pnpm`
# - name: Install pnpm
# uses: pnpm/action-setup@v2
# with:
# version: 8
- name: build
env:
BASE_PATH: '/your-repo-name'
run: |
npm run build
touch build/.nojekyll
63
sveltekit
deploy:
needs: build_site
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy
id: deployment
uses: actions/deploy-pages@v1
Go to TOC
64
Single-page apps
Single-page apps
You can turn any SvelteKit app, using any adapter, into a fully client-rendered single-page app (SPA) by dis‐
abling SSR at the root layout:
In most situations this is not recommended: it harms SEO, tends to slow down perceived perfor‐
mance, and makes your app inaccessible to users if JavaScript fails or is disabled (which happens
more often than you probably think).
If you don't have any server-side logic (i.e. +page.server.js , +layout.server.js or +server.js files)
you can use adapter-static to create your SPA by adding a fallback page.
Usage
Install with npm i -D @sveltejs/adapter-static , then add the adapter to your svelte.config.js with
the following options:
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
fallback: '200.html' // may differ from host to host
})
}
};
The fallback page is an HTML page created by SvelteKit from your page template (e.g. app.html ) that
loads your app and navigates to the correct route. For example Surge, a static web host, lets you add a
200.html file that will handle any requests that don't correspond to static assets or prerendered pages.
On some hosts it may be index.html or something else entirely — consult your platform's documentation.
Apache
To run an SPA on Apache, you should add a static/.htaccess file to route requests to the fallback page:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^200\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
65
sveltekit
66
Cloudflare Pages
Cloudflare Pages
To deploy to Cloudflare Pages, use adapter-cloudflare .
This adapter will be installed by default when you use adapter-auto . If you plan on staying with
Cloudflare Pages you can switch from adapter-auto to using this adapter directly so that type declarations
will be automatically applied and you can set Cloudflare-specific options.
Comparisons
adapter-cloudflare – supports all SvelteKit features; builds for Cloudflare Pages
adapter-cloudflare-workers – supports all SvelteKit features; builds for Cloudflare Workers
adapter-static – only produces client-side static assets; compatible with Cloudflare Pages
Usage
Install with npm i -D @sveltejs/adapter-cloudflare , then add the adapter to your svelte.con‐
fig.js :
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';
export default {
kit: {
adapter: adapter({
// See below for an explanation of these options
routes: {
include: ['/*'],
exclude: ['<all>']
}
})
}
};
Options
The routes option allows you to customise the _routes.json file generated by adapter-cloudflare .
include defines routes that will invoke a function, and defaults to ['/*']
exclude defines routes that will not invoke a function — this is a faster and cheaper way to serve your
app's static assets. This array can include the following special values:
<build> contains your app's build artifacts (the files generated by Vite)
<files> contains the contents of your static directory
<prerendered> contains a list of prerendered pages
<all> (the default) contains all of the above
67
sveltekit
You can have up to 100 include and exclude rules combined. Generally you can omit the routes op‐
tions, but if (for example) your <prerendered> paths exceed that limit, you may find it helpful to manually
create an exclude list that includes '/articles/*' instead of the auto-generated ['/articles/foo',
'/articles/bar', '/articles/baz', ...] .
Deployment
Please follow the Get Started Guide for Cloudflare Pages to begin.
When configuring your project settings, you must use the following settings:
Bindings
The env object contains your project's bindings, which consist of KV/DO namespaces, etc. It is passed to
SvelteKit via the platform property, along with context and caches , meaning that you can access it in
hooks and endpoints:
// @errors: 7031
export async function POST({ request, platform }) {
const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x');
}
To make these types available to your app, reference them in your src/app.d.ts :
export {};
68
Cloudflare Pages
Testing Locally
platform.env is only available in the final build and not in dev mode. For testing the build, you can use
wrangler version 3. Once you have built your site, run wrangler pages dev .svelte-kit/cloudflare .
Ensure you have your bindings in your wrangler.toml .
Notes
Functions contained in the /functions directory at the project's root will not be included in the deploy‐
ment, which is compiled to a single _worker.js file. Functions should be implemented as server endpoints
in your SvelteKit app.
The _headers and _redirects files specific to Cloudflare Pages can be used for static asset responses
(like images) by putting them into the /static folder.
However, they will have no effect on responses dynamically rendered by SvelteKit, which should return cus‐
tom headers or redirect responses from server endpoints or with the handle hook.
Troubleshooting
Further reading
You may wish to refer to Cloudflare's documentation for deploying a SvelteKit site.
Go to TOC
69
sveltekit
Cloudflare Workers
To deploy to Cloudflare Workers, use adapter-cloudflare-workers .
Unless you have a specific reason to use adapter-cloudflare-workers , it's recommended that you
use adapter-cloudflare instead. Both adapters have equivalent functionality, but Cloudflare Pages
offers features like GitHub integration with automatic builds and deploys, preview deployments, in‐
stant rollback and so on.
Usage
Install with npm i -D @sveltejs/adapter-cloudflare-workers , then add the adapter to your svelte.‐
config.js :
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare-workers';
export default {
kit: {
adapter: adapter()
}
};
Basic Configuration
This adapter expects to find a wrangler.toml file in the project root. It should look something like this:
main = "./.cloudflare/worker.js"
site.bucket = "./.cloudflare/public"
compatibility_date = "2021-11-12"
workers_dev = true
<your-service-name> can be anything. <your-account-id> can be found by logging into your Cloudflare
dashboard and grabbing it from the end of the URL:
https://dash.cloudflare.com/<your-account-id>
70
Cloudflare Workers
You should add the .cloudflare directory (or whichever directories you specified for main and sit
e.bucket ) to your .gitignore .
You will need to install wrangler and log in, if you haven't already:
npm i -g wrangler
wrangler login
wrangler publish
Custom config
If you would like to use a config file other than wrangler.toml , you can do like so:
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare-workers';
export default {
kit: {
adapter: adapter({ config: '<your-wrangler-name>.toml' })
}
};
Bindings
The env object contains your project's bindings, which consist of KV/DO namespaces, etc. It is passed to
SvelteKit via the platform property, along with context and caches , meaning that you can access it in
hooks and endpoints:
// @errors: 7031
export async function POST({ request, platform }) {
const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x');
}
To make these types available to your app, reference them in your src/app.d.ts :
71
sveltekit
+ };
}
}
}
export {};
Testing Locally
platform.env is only available in the final build and not in dev mode. For testing the build, you can use
wrangler. Once you have built your site, run wrangler dev . Ensure you have your bindings in your wran‐
gler.toml . Wrangler version 3 is recommended.
Troubleshooting
Worker size limits
When deploying to workers, the server generated by SvelteKit is bundled into a single file. Wrangler will fail
to publish your worker if it exceeds the size limits after minification. You're unlikely to hit this limit usually,
but some large libraries can cause this to happen. In that case, you can try to reduce the size of your work‐
er by only importing such libraries on the client side. See the FAQ for more information.
Go to TOC
72
Netlify
Netlify
To deploy to Netlify, use adapter-netlify .
This adapter will be installed by default when you use adapter-auto , but adding it to your project allows
you to specify Netlify-specific options.
Usage
Install with npm i -D @sveltejs/adapter-netlify , then add the adapter to your svelte.config.js :
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-netlify';
export default {
kit: {
// default options are shown
adapter: adapter({
// if true, will create a Netlify Edge Function rather
// than using standard Node-based functions
edge: false,
Then, make sure you have a netlify.toml file in the project root. This will determine where to write static as‐
sets based on the build.publish settings, as per this sample configuration:
[build]
command = "npm run build"
publish = "build"
If the netlify.toml file or the build.publish value is missing, a default value of "build" will be used.
Note that if you have set the publish directory in the Netlify UI to something else then you will need to set it
in netlify.toml too, or use the default value of "build" .
Node version
New projects will use Node 16 by default. However, if you're upgrading a project you created a while ago it
may be stuck on an older version. See the Netlify docs for details on manually specifying Node 16 or newer.
73
sveltekit
// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-netlify';
export default {
kit: {
adapter: adapter({
// will create a Netlify Edge Function using Deno-based
// rather than using standard Node-based functions
edge: true
})
}
};
Redirect rules
During compilation, redirect rules are automatically appended to your _redirects file. (If it doesn't exist
yet, it will be created.) That means:
[[redirects]] in netlify.toml will never match as _redirects has a higher priority. So always put
your rules in the _redirects file.
_redirects shouldn't have any custom "catch all" rules such as /* /foobar/:splat . Otherwise the
automatically appended rule will never be applied as Netlify is only processing the first matching rule.
Netlify Forms
1. Create your Netlify HTML form as described here, e.g. as /routes/contact/+page.svelte . (Don't for‐
get to add the hidden form-name input element!)
2. Netlify's build bot parses your HTML files at deploy time, which means your form must be prerendered as
HTML. You can either add export const prerender = true to your contact.svelte to prerender
just that page or set the kit.prerender.force: true option to prerender all pages.
3. If your Netlify form has a custom success message like <form netlify ... action="/success"> then
ensure the corresponding /routes/success/+page.svelte exists and is prerendered.
74
Netlify
Netlify Functions
With this adapter, SvelteKit endpoints are hosted as Netlify Functions. Netlify function handlers have addi‐
tional context, including Netlify Identity information. You can access this context via the event.platform.‐
context field inside your hooks and +page.server or +layout.server endpoints. These are serverless
functions when the edge property is false in the adapter config or edge functions when it is true .
Additionally, you can add your own Netlify functions by creating a directory for them and adding the config‐
uration to your netlify.toml file. For example:
[build]
command = "npm run build"
publish = "build"
[functions]
directory = "functions"
Troubleshooting
Accessing the file system
You can't access the file system through methods like fs.readFileSync in Serverless/Edge environments.
If you need to access files that way, do that during building the app through prerendering. If you have a
blog for example and don't want to manage your content through a CMS, then you need to prerender the
content (or prerender the endpoint from which you get it) and redeploy your blog everytime you add new
content.
Go to TOC
75
sveltekit
Vercel
To deploy to Vercel, use adapter-vercel .
This adapter will be installed by default when you use adapter-auto , but adding it to your project allows
you to specify Vercel-specific options.
Usage
Install with npm i -D @sveltejs/adapter-vercel , then add the adapter to your svelte.config.js :
export default {
kit: {
adapter: adapter({
// see the 'Deployment configuration' section below
})
}
};
Deployment configuration
To control how your routes are deployed to Vercel as functions, you can specify deployment configuration,
either through the option shown above or with export const config inside +server.js ,
+page(.server).js and +layout(.server).js files.
For example you could deploy some parts of your app as Edge Functions...
...and others as Serverless Functions (note that by specifying config inside a layout, it applies to all child
pages):
76
Vercel
external : an array of dependencies that esbuild should treat as external when bundling functions. This
should only be used to exclude optional dependencies that will not run outside Node
memory : the amount of memory available to the function. Defaults to 1024 Mb, and can be decreased
to 128 Mb or increased in 64Mb increments up to 3008 Mb on Pro or Enterprise accounts
maxDuration : maximum execution duration of the function. Defaults to 10 seconds for Hobby ac‐
counts, 60 for Pro and 900 for Enterprise
isr : configuration Incremental Static Regeneration, described below
If your functions need to access data in a specific region, it's recommended that they be deployed in the
same region (or close to it) for optimal performance.
To add ISR to a route, include the isr property in your config object:
// @filename: index.js
// cut---
import { BYPASS_TOKEN } from '$env/static/private';
// Random token that can be provided in the URL to bypass the cached
version of the asset, by requesting the asset
// with a __prerender_bypass=<token> cookie.
77
sveltekit
//
// Making a `GET` or `HEAD` request with `x-prerender-revalidate: <token>`
will force the asset to be re-validated.
bypassToken: BYPASS_TOKEN,
Environment variables
Vercel makes a set of deployment-specific environment variables available. Like other environment vari‐
ables, these are accessible from $env/static/private and $env/dynamic/private (sometimes — more
on that later), and inaccessible from their public counterparts. To access one of these variables from the
client:
// @errors: 2305
/// file: +layout.server.js
import { VERCEL_COMMIT_REF } from '$env/static/private';
Since all of these variables are unchanged between build time and run time when building on Vercel, we
recommend using $env/static/private — which will statically replace the variables, enabling optimisa‐
tions like dead code elimination — rather than $env/dynamic/private .
Notes
Vercel functions
If you have Vercel functions contained in the api directory at the project's root, any requests for /api/*
will not be handled by SvelteKit. You should implement these as API routes in your SvelteKit app instead,
unless you need to use a non-JavaScript language in which case you will need to ensure that you don't have
any /api/* routes in your SvelteKit app.
78
Vercel
Node version
Projects created before a certain date will default to using Node 14, while SvelteKit requires Node 16 or lat‐
er. You can change the Node version in your project settings.
Troubleshooting
Accessing the file system
You can't access the file system through methods like fs.readFileSync in Serverless/Edge environments.
If you need to access files that way, do that during building the app through prerendering. If you have a
blog for example and don't want to manage your content through a CMS, then you need to prerender the
content (or prerender the endpoint from which you get it) and redeploy your blog everytime you add new
content.
Go to TOC
79
sveltekit
Writing adapters
If an adapter for your preferred environment doesn't yet exist, you can build your own. 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 :
// @filename: ambient.d.ts
type AdapterSpecificOptions = any;
// @filename: index.js
// cut---
/** @param {AdapterSpecificOptions} options */
export default function (options) {
/** @type {import('@sveltejs/kit').Adapter} */
const adapter = {
name: 'adapter-package-name',
async adapt(builder) {
// adapter implementation
}
};
return adapter;
}
Within the adapt method, there are a number of things that an adapter should do:
Where possible, we recommend putting the adapter output under the build/ directory with any intermedi‐
ate output placed under .svelte-kit/[adapter-name] .
Go to TOC
80
Advanced routing
Advanced routing
Rest parameters
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]
// @noErrors
{
org: 'sveltejs',
repo: 'kit',
branch: 'master',
file: 'documentation/docs/04-advanced-routing.md'
}
404 pages
Rest parameters also allow you to render custom 404s. Given these routes...
src/routes/
├ marx-brothers/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
...the marx-brothers/+error.svelte file will not be rendered if you visit /marx-brothers/karl , be‐
cause no route was matched. If you want to render the nested error page, you should create a route that
matches any /marx-brothers/* request, and return a 404 from it:
src/routes/
├ marx-brothers/
+| ├ [...path]/
│ ├ chico/
│ ├ harpo/
│ ├ groucho/
│ └ +error.svelte
└ +error.svelte
81
sveltekit
Optional parameters
A route like [lang]/home contains a parameter named lang which is required. Sometimes it's beneficial
to make these parameters optional, so that in this example both home and en/home point to the same
page. You can do that by wrapping the parameter in another bracket pair: [[lang]]/home
Note that an optional route parameter cannot follow a rest parameter ( [...rest]/[[optional]] ), since
parameters are matched 'greedily' and the optional parameter would always be unused.
Matching
A route like src/routes/archive/[page] would match /archive/3 , but it would also match
/archive/potato . We don't want that. You can ensure that route parameters are well-formed by adding a
matcher — which takes the parameter string ( "3" or "potato" ) and returns true if it is valid — to your
params directory...
-src/routes/archive/[page]
+src/routes/archive/[page=integer]
If the pathname doesn't match, SvelteKit will try to match other routes (using the sort order specified
below), before eventually returning a 404.
Each module in the params directory corresponds to a matcher, with the exception of *.test.js and
*.spec.js files which may be used to unit test your matchers.
82
Advanced routing
Sorting
It's possible for multiple routes to match a given path. For example each of these routes would match
/foo-abc :
src/routes/[...catchall]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/foo-abc/+page.svelte
SvelteKit needs to know which route is being requested. To do so, it sorts them according to the following
rules...
More specific routes are higher priority (e.g. a route with no parameters is more specific than a route
with one dynamic parameter, and so on)
Parameters with matchers ( [name=type] ) are higher priority than those without ( [name] )
[[optional]] and [...rest] parameters are ignored unless they are the final part of the route, in
which case they are treated with lowest priority. In other words x/[[y]]/z is treated equivalently to
x/z for the purposes of sorting
Ties are resolved alphabetically
...resulting in this ordering, meaning that /foo-abc will invoke src/routes/foo-abc/+page.svelte , and
/foo-def will invoke src/routes/foo-[c]/+page.svelte rather than less specific routes:
src/routes/foo-abc/+page.svelte
src/routes/foo-[c]/+page.svelte
src/routes/[[a=x]]/+page.svelte
src/routes/[b]/+page.svelte
src/routes/[...catchall]/+page.svelte
Encoding
Some characters can't be used on the filesystem — / on Linux and Mac, \ / : * ? " < > | on
Windows. The # and % characters have special meaning in URLs, and the [ ] ( ) characters have spe‐
cial meaning to SvelteKit, so these also can't be used directly as part of your route.
To use these characters in your routes, you can use hexadecimal escape sequences, which have the format
[x+nn] where nn is a hexadecimal character code:
\ — [x+5c]
/ — [x+2f]
: — [x+3a]
* — [x+2a]
? — [x+3f]
" — [x+22]
< — [x+3c]
> — [x+3e]
83
sveltekit
| — [x+7c]
# — [x+23]
% — [x+25]
[ — [x+5b]
] — [x+5d]
( — [x+28]
) — [x+29]
You can determine the hexadecimal code for a character with JavaScript:
You can also use Unicode escape sequences. Generally you won't need to as you can use the unencoded
character directly, but if — for some reason — you can't have a filename with an emoji in it, for example,
then you can use the escaped characters. In other words, these are equivalent:
src/routes/[u+d83e][u+dd2a]/+page.svelte
🤪
src/routes/ /+page.svelte
The format for a Unicode escape sequence is [u+nnnn] where nnnn is a valid value between 0000 and
10ffff . (Unlike JavaScript string escaping, there's no need to use surrogate pairs to represent code points
above ffff .) To learn more about Unicode encodings, consult Programming with Unicode.
Since TypeScript struggles with directories with a leading . character, you may find it useful to en‐
code these characters when creating e.g. .well-known routes: src/routes/[x+2e]well-know
n/...
Advanced layouts
By default, the layout hierarchy mirrors the route hierarchy. In some cases, that might not be what you
want.
(group)
Perhaps you have some routes that are 'app' routes that should have one layout (e.g. /dashboard or
/item ), and others that are 'marketing' routes that should have a different layout ( /blog or /testimo‐
nials ). We can group these routes with a directory whose name is wrapped in parentheses — unlike nor‐
mal directories, (app) and (marketing) do not affect the URL pathname of the routes inside them:
src/routes/
+│ (app)/
│ ├ dashboard/
84
Advanced routing
│ ├ item/
│ └ +layout.svelte
+│ (marketing)/
│ ├ about/
│ ├ testimonials/
│ └ +layout.svelte
├ admin/
└ +layout.svelte
You can also put a +page directly inside a (group) , for example if / should be an (app) or a (market‐
ing) page.
In the example above, the /admin route does not inherit either the (app) or (marketing) layouts.
+page@
Pages can break out of the current layout hierarchy on a route-by-route basis. Suppose we have an
/item/[id]/embed route inside the (app) group from the previous example:
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
+│ │ │ │ └ +page.svelte
│ │ │ └ +layout.svelte
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte
Ordinarily, this would inherit the root layout, the (app) layout, the item layout and the [id] layout. We
can reset to one of those layouts by appending @ followed by the segment name — or, for the root layout,
the empty string. In this example, we can choose from the following options:
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
+│ │ │ │ └ +page@(app).svelte
│ │ │ └ +layout.svelte
85
sveltekit
│ │ └ +layout.svelte
│ └ +layout.svelte
└ +layout.svelte
+layout@
Like pages, layouts can themselves break out of their parent layout hierarchy, using the same technique.
For example, a [email protected] component would reset the hierarchy for all its child routes.
src/routes/
├ (app)/
│ ├ item/
│ │ ├ [id]/
│ │ │ ├ embed/
│ │ │ │ └ +page.svelte // uses (app)/item/[id]/+layout.svelte
│ │ │ ├ +layout.svelte // inherits from (app)/item/[email protected]
│ │ │ └ +page.svelte // uses (app)/item/[email protected]
│ │ └ [email protected] // inherits from root layout, skipping
(app)/+layout.svelte
│ └ +layout.svelte
└ +layout.svelte
<ReusableLayout {data}>
<slot />
</ReusableLayout>
86
Advanced routing
Further reading
Tutorial: Advanced Routing
Go to TOC
87
sveltekit
Hooks
'Hooks' are app-wide functions you declare that SvelteKit will call in response to specific events, giving you
fine-grained control over the framework's behaviour.
Code in these modules will run when the application starts up, making them useful for initializing database
clients and so on.
Server hooks
The following hooks can be added to src/hooks.server.js :
handle
This function runs every time the SvelteKit server receives a request — whether that happens while the app
is running, or during prerendering — and determines the response. It receives an event object represent‐
ing the request and a function called resolve , which renders the route and generates a Response . This
allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes pro‐
grammatically, for example).
Requests for static assets — which includes pages that were already prerendered — are not handled
by SvelteKit.
88
Hooks
If unimplemented, defaults to ({ event, resolve }) => resolve(event) . To add custom data to the re‐
quest, which is passed to handlers in +server.js and server load functions, populate the event.locals
object, as shown below.
// @filename: index.js
// cut---
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
event.locals.user = await getUserInformation(event.cookies.get('sessionid'));
return response;
}
You can define multiple handle functions and execute them 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:
89
sveltekit
return response;
}
Note that resolve(...) will never throw an error, it will always return a Promise<Response> with the ap‐
propriate status code. If an error is thrown elsewhere during handle , it is treated as fatal, and SvelteKit
will respond with a JSON representation of the error or a fallback error page — which can be customised via
src/error.html — depending on the Accept header. You can read more about error handling here.
handleFetch
This function allows you to modify (or replace) a fetch request that happens inside a load or action
function that runs on the server (or during pre-rendering).
For example, your load function might make a request to a public URL like https://api.yourapp.com
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).
return fetch(request);
}
Credentials
For same-origin requests, SvelteKit's fetch implementation will forward cookie and authorization
headers unless the credentials option is set to "omit" .
For cross-origin requests, cookie will be included if the request URL belongs to a subdomain of the app —
for example if your app is on my-domain.com , and your API is on api.my-domain.com , cookies will be in‐
cluded in the request.
90
Hooks
If your app and your API are on sibling subdomains — www.my-domain.com and api.my-domain.com for
example — then a cookie belonging to a common parent domain like my-domain.com will not be included,
because SvelteKit has no way to know which domain the cookie belongs to. In these cases you will need to
manually include the cookie using handleFetch :
return fetch(request);
}
Shared hooks
The following can be added to src/hooks.server.js and src/hooks.client.js :
handleError
If an unexpected error is thrown during loading or rendering, this function will be called with the error
and the event . This allows for two things:
The following code shows an example of typing the error shape as { message: string; errorId: string
} and returning it accordingly from the handleError functions:
export {};
91
sveltekit
// @filename: index.js
// cut---
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
Sentry.init({/*...*/})
return {
message: 'Whoops!',
errorId
};
}
// @filename: index.js
// cut---
import * as Sentry from '@sentry/svelte';
Sentry.init({/*...*/})
return {
message: 'Whoops!',
errorId
};
}
This function is not called for expected errors (those thrown with the error function imported from
@sveltejs/kit ).
92
Hooks
During development, if an error occurs because of a syntax error in your Svelte code, the passed in error
has a frame property appended highlighting the location of the error.
Further reading
Tutorial: Hooks
Go to TOC
93
sveltekit
Errors
Errors are an inevitable fact of software development. SvelteKit handles errors differently depending on
where they occur, what kind of errors they are, and the nature of the incoming request.
Error objects
SvelteKit distinguishes between expected and unexpected errors, both of which are represented as simple {
message: string } objects by default.
You can add additional properties, like a code or a tracking id , as shown in the examples below. (When
using TypeScript this requires you to redefine the Error type as described in type safety).
Expected errors
An expected error is one created with the error helper imported from @sveltejs/kit :
// @filename: index.js
// cut---
import { error } from '@sveltejs/kit';
import * as db from '$lib/server/database';
if (!post) {
throw error(404, {
message: 'Not found'
});
}
return { post };
}
This tells SvelteKit to set the response status code to 404 and render an +error.svelte component,
where $page.error is the object provided as the second argument to error(...) .
<h1>{$page.error.message}</h1>
94
Errors
throw error(404, {
message: 'Not found',
+ code: 'NOT_FOUND'
});
...otherwise, for convenience, you can pass a string as the second argument:
Unexpected errors
An unexpected error is any other exception that occurs while handling a request. Since these can contain
sensitive information, unexpected error messages and stack traces are not exposed to users.
By default, unexpected errors are printed to the console (or, in production, your server logs), while the error
that is exposed to the user has a generic shape:
Unexpected errors will go through the handleError hook, where you can add your own error handling —
for example, sending errors to a reporting service, or returning a custom error object.
// @filename: index.js
// cut---
import * as Sentry from '@sentry/node';
Sentry.init({/*...*/})
return {
message: 'Whoops!',
code: error?.code ?? 'UNKNOWN'
};
}
95
sveltekit
Responses
If an error occurs inside handle or inside a +server.js request handler, SvelteKit will respond with either
a fallback error page or a JSON representation of the error object, depending on the request's Accept
headers.
You can customise the fallback error page by adding a src/error.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>%sveltekit.error.message%</title>
</head>
<body>
<h1>My custom error page</h1>
<p>Status: %sveltekit.status%</p>
<p>Message: %sveltekit.error.message%</p>
</body>
</html>
If the error instead occurs inside a load function while rendering a page, SvelteKit will render the +er‐
ror.svelte component nearest to where the error occurred. If the error occurs inside a load function in
+layout(.server).js , the closest error boundary in the tree is an +error.svelte file above that layout
(not next to it).
The exception is when the error occurs inside the root +layout.js or +layout.server.js , since the root
layout would ordinarily contain the +error.svelte component. In this case, SvelteKit uses the fallback er‐
ror page.
Type safety
If you're using TypeScript and need to customize the shape of errors, you can do so by declaring an
App.Error interface in your app (by convention, in src/app.d.ts , though it can live anywhere that
TypeScript can 'see'):
export {};
96
Errors
Further reading
Tutorial: Errors and redirects
Tutorial: Hooks
Go to TOC
97
sveltekit
Link options
In SvelteKit, <a> elements (rather than framework-specific <Link> components) are used to navigate be‐
tween the routes of your app. If the user clicks on a link whose href is 'owned' by the app (as opposed to,
say, a link to an external site) then SvelteKit will navigate to the new page by importing its code and then
calling any load functions it needs to fetch data.
You can customise the behaviour of links with data-sveltekit-* attributes. These can be applied to the
<a> itself, or to a parent element.
data-sveltekit-preload-data
Before the browser registers that the user has clicked on a link, we can detect that they've hovered the
mouse over it (on desktop) or that a touchstart or mousedown event was triggered. In both cases, we
can make an educated guess that a click event is coming.
SvelteKit can use this information to get a head start on importing the code and fetching the page's data,
which can give us an extra couple of hundred milliseconds — the difference between a user interface that
feels laggy and one that feels snappy.
We can control this behaviour with the data-sveltekit-preload-data attribute, which can have one of
two values:
"hover" means that preloading will start if the mouse comes to a rest over a link. On mobile, preload‐
ing begins on touchstart
"tap" means that preloading will start as soon as a touchstart or mousedown event is registered
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
Sometimes, calling load when the user hovers over a link might be undesirable, either because it's likely
to result in false positives (a click needn't follow a hover) or because data is updating very quickly and a de‐
lay could mean staleness.
In these cases, you can specify the "tap" value, which causes SvelteKit to call load only when the user
taps or clicks on a link:
98
Link options
Data will never be preloaded if the user has chosen reduced data usage, meaning navigator.connec‐
tion.saveData is true .
data-sveltekit-preload-code
Even in cases where you don't want to preload data for a link, it can be beneficial to preload the code. The
data-sveltekit-preload-code attribute works similarly to data-sveltekit-preload-data , except that
it can take one of four values, in decreasing 'eagerness':
Note that viewport and eager only apply to links that are present in the DOM immediately following nav‐
igation — if a link is added later (in an {#if ...} block, for example) it will not be preloaded until trig‐
gered by hover or tap . This is to avoid performance pitfalls resulting from aggressively observing the
DOM for changes.
Since preloading code is a prerequisite for preloading data, this attribute will only have an effect if it
specifies a more eager value than any data-sveltekit-preload-data attribute that is present.
As with data-sveltekit-preload-data , this attribute will be ignored if the user has chosen reduced data
usage.
data-sveltekit-reload
Occasionally, we need to tell SvelteKit not to handle a link, but allow the browser to handle it. Adding a
data-sveltekit-reload attribute to a link...
Links with a rel="external" attribute will receive the same treatment. In addition, they will be ignored
during prerendering.
99
sveltekit
data-sveltekit-replacestate
Sometimes you don't want navigation to create a new entry in the browser's session history. Adding a
data-sveltekit-replacestate attribute to a link...
...will replace the current history entry rather than creating a new one with pushState when the link is
clicked.
data-sveltekit-keepfocus
Sometimes you don't want focus to be reset after navigation. For example, maybe you have a search form
that submits as the user is typing, and you want to keep focus on the text input. Adding a data-sveltek‐
it-keepfocus attribute to it...
<form data-sveltekit-keepfocus>
<input type="text" name="query">
</form>
...will cause the currently focused element to retain focus after navigation. In general, avoid using this at‐
tribute on links, since the focused element would be the <a> tag (and not a previously focused element)
and screen reader and other assistive technology users often expect focus to be moved after a navigation.
You should also only use this attribute on elements that still exist after navigation. If the element no longer
exists, the user's focus will be lost, making for a confusing experience for assistive technology users.
data-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 data-sveltekit-noscroll attribute to
a link...
Disabling options
To disable any of these options inside an element where they have been enabled, use the "false" value:
<div data-sveltekit-preload-data>
<!-- these links will be preloaded -->
<a href="/a">a</a>
<a href="/b">b</a>
<a href="/c">c</a>
100
Link options
<div data-sveltekit-preload-data="false">
<!-- these links will NOT be preloaded -->
<a href="/d">d</a>
<a href="/e">e</a>
<a href="/f">f</a>
</div>
</div>
To apply an attribute to an element conditionally, do this ( "true" and "false" are both accepted values):
<div data-sveltekit-reload={shouldReload}>
```<span style='float: footnote;'><a href="../../index.html#toc">Go to TOC</a>
</span>
101
sveltekit
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.
You can disable automatic registration if you need to register the service worker with your own logic or use
another solution. The default registration looks something like this:
if ('serviceWorker' in navigator) {
addEventListener('load', function () {
navigator.serviceWorker.register('./path/to/service-worker.js');
});
}
The following example caches the built app and any files in static eagerly, and caches all other requests
as they happen. This would make each page work offline once visited.
// @errors: 2339
/// <reference types="@sveltejs/kit" />
import { build, files, version } from '$service-worker';
const ASSETS = [
...build, // the app itself
...files // everything in `static`
];
event.waitUntil(addFilesToCache());
});
102
Service workers
event.waitUntil(deleteOldCaches());
});
return response;
} catch {
return cache.match(event.request);
}
}
event.respondWith(respond());
});
Be careful when caching! In some cases, stale data might be worse than data that's unavailable while
offline. Since browsers will empty caches if they get too full, you should also be careful about caching
large assets like video files.
During development
The service worker is bundled for production, but not during development. For that reason, only browsers
that support modules in service workers will be able to use them at dev time. If you are manually register‐
ing your service worker, you will need to pass the { type: 'module' } option in development:
103
sveltekit
navigator.serviceWorker.register('/service-worker.js', {
type: dev ? 'module' : 'classic'
});
Type safety
Setting up proper types for service workers requires some manual setup. Inside your service-worker.js ,
add the following to the top of your file:
This disables access to DOM typings like HTMLElement which are not available inside a service worker and
instantiates the correct globals. The reassignment of self to sw allows you to type cast it in the process
(there are a couple of ways to do this, but the easiest that requires no additional files). Use sw instead of
self in the rest of the file. The reference to the SvelteKit types ensures that the $service-worker import
has proper type definitions.
Other solutions
SvelteKit's service worker implementation is deliberately low-level. If you need a more full-flegded but also
more opinionated solution, we recommend looking at solutions like Vite PWA plugin, which uses Workbox.
For more general information on service workers, we recommend the MDN web docs.
Go to TOC
104
Server-only modules
Server-only modules
Like a good friend, SvelteKit keeps your secrets. When writing your backend and frontend in the same
repository, it can be easy to accidentally import sensitive data into your front-end code (environment vari‐
ables containing API keys, for example). SvelteKit provides a way to prevent this entirely: server-only
modules.
Your modules
You can make your own modules server-only in two ways:
How it works
Any time you have public-facing code that imports server-only code (whether directly or indirectly)...
// @errors: 7005
/// file: $lib/server/secrets.js
export const atlantisCoordinates = [/* redacted */];
105
sveltekit
Even though the public-facing code — src/routes/+page.svelte — only uses the add export and not the
secret atlantisCoordinates export, the secret code could end up in JavaScript that the browser down‐
loads, and so the import chain is considered unsafe.
This feature also works with dynamic imports, even interpolated ones like await import(`./${foo}.js`) ,
with one small caveat: during development, if there are two or more dynamic imports between the public-
facing code and the server-only module, the illegal import will not be detected the first time the code is
loaded.
Unit testing frameworks like Vitest do not distinguish between server-only and public-facing code. For
this reason, illegal import detection is disabled when running tests, as determined by process.env.T
EST === 'true' .
Further reading
Tutorial: Environment variables
Go to TOC
106
Asset handling
Asset handling
Caching and inlining
Vite will automatically process imported assets for improved performance. Hashes will be added to the file‐
names so that they can be cached and assets smaller than assetsInlineLimit will be inlined.
<script>
import logo from '$lib/assets/logo.png';
</script>
If you prefer to reference assets directly in the markup, you can use a preprocessor such as svelte-prepro‐
cess-import-assets.
For assets included via the CSS url() function, you may find vitePreprocess useful.
Transforming
You may wish to transform your images to output compressed image formats such as .webp or .avif , re‐
sponsive images with different sizes for different devices, or images with the EXIF data stripped for privacy.
For images that are included statically, you may use a Vite plugin such as vite-imagetools. You may also
consider a CDN, which can serve the appropriate transformed image based on the Accept HTTP header
and query string parameters.
Go to TOC
107
sveltekit
Snapshots
Ephemeral DOM state — like scroll positions on sidebars, the content of <input> elements and so on — is
discarded when you navigate from one page to another.
For example, if the user fills out a form but clicks a link before submitting, then hits the browser's back but‐
ton, the values they filled in will be lost. In cases where it's valuable to preserve that input, you can take a
snapshot of DOM state, which can then be restored if the user navigates back.
To do this, export a snapshot object with capture and restore methods from a +page.svelte or
+layout.svelte :
<form method="POST">
<label for="comment">Comment</label>
<textarea id="comment" bind:value={comment} />
<button>Post comment</button>
</form>
When you navigate away from this page, the capture function is called immediately before the page up‐
dates, and the returned value is associated with the current entry in the browser's history stack. If you nav‐
igate back, the restore function is called with the stored value as soon as the page is updated.
The data must be serializable as JSON so that it can be persisted to sessionStorage . This allows the state
to be restored when the page is reloaded, or when the user navigates back from a different site.
Avoid returning very large objects from capture — once captured, objects will be retained in memo‐
ry for the duration of the session, and in extreme cases may be too large to persist to sessionStora
ge .
Go to TOC
108
Packaging
Packaging
You can use SvelteKit to build apps as well as component libraries, using the @sveltejs/package package
( npm create svelte has an option to set this up for you).
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 component library has the exact same structure as a SvelteKit app, except that src/lib is the public-
facing bit, and your root package.json is used to publish the package. src/routes might be a documen‐
tation or demo site that accompanies the library, or it might just be a sandbox you use during development.
Running the svelte-package command from @sveltejs/package will take the contents of src/lib and
generate a dist directory (which can be configured) containing the following:
All the files in src/lib . 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 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 — people using your library might use TypeScript, for which they require these type definition files.
@sveltejs/package version 1 generated a package.json . This is no longer the case and it will now
use the package.json from your project and validate that it is correct instead. If you're still on ver‐
sion 1, see this PR for migration instructions.
Anatomy of a package.json
Since you're now building a library for public use, the contents of your package.json will become more
important. Through it, you configure the entry points of your package, which files are published to npm, and
which dependencies your library has. Let's go through the most important fields one by one.
name
This is the name of your package. It will be available for others to install using that name, and visible on
https://npmjs.com/package/<name> .
{
"name": "your-library"
}
109
sveltekit
license
Every package should have a license field so people know how they are allowed to use it. A very popular li‐
cense which is also very permissive in terms of distribution and reuse without warranty is MIT .
{
"license": "MIT"
}
Read more about it here. Note that you should also include a LICENSE file in your package.
files
This tells npm which files it will pack up and upload to npm. It should contain your output folder ( dist by
default). Your package.json and README and LICENSE will always be included, so you don't need to
specify them.
{
"files": ["dist"]
}
To exclude unnecessary files (such as unit tests, or modules that are only imported from src/routes etc)
you can add them to an .npmignore file. This will result in smaller packages that are faster to install.
exports
The "exports" field contains the package's entry points. If you set up a new library project through npm
create svelte@latest , it's set to a single export, the package root:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
}
}
This tells bundlers and tooling that your package only has one entry point, the root, and everything should
be imported through that, like this:
// @errors: 2307
import { Something } from 'your-library';
The types and svelte keys are export conditions. They tell tooling what file to import when they look up
the your-library import:
110
Packaging
TypeScript sees the types condition and looks up the type definition file. If you don't publish type defin‐
itions, omit this condition.
Svelte-aware tooling sees the svelte condition and knows this is a Svelte component library. If you
publish a library that does not export any Svelte components and that could also work in non-Svelte
projects (for example a Svelte store library), you can replace this condition with default .
Previous versions of @sveltejs/package also added a package.json export. This is no longer part
of the template because all tooling can now deal with a package.json not being explicitly exported.
You can adjust exports to your liking and provide more entry points. For example, if instead of a
src/lib/index.js file that re-exported components you wanted to expose a src/lib/Foo.svelte com‐
ponent directly, you could create the following export map...
{
"exports": {
"./Foo.svelte": {
"types": "./dist/Foo.svelte.d.ts",
"svelte": "./dist/Foo.svelte"
}
}
}
...and a consumer of your library could import the component like so:
// @filename: ambient.d.ts
declare module 'your-library/Foo.svelte';
// @filename: index.js
// cut---
import Foo from 'your-library/Foo.svelte';
Beware that doing this will need additional care if you provide type definitions. Read more about the
caveat here
In general, each key of the exports map is the path the user will have to use to import something from your
package, and the value is the path to the file that will be imported or a map of export conditions which in
turn contains these file paths.
svelte
This is a legacy field that enabled tooling to recognise Svelte component libraries. It's no longer necessary
when using the svelte export condition, but for backwards compatibility with outdated tooling that doesn't
yet know about export conditions it's good to keep it around. It should point towards your root entry point.
111
sveltekit
{
"svelte": "./dist/index.js"
}
TypeScript
You should ship type definitions for your library even if you don't use TypeScript yourself so that people who
do get proper intellisense when using your library. @sveltejs/package makes the process of generating
types mostly opaque to you. By default, when packaging your library, type definitions are auto-generated
for JavaScript, TypeScript and Svelte files. All you need to ensure is that the types condition in the exports
map points to the correct files. When initialising a library project through npm create svelte@latest , this
is automatically setup for the root export.
If you have something else than a root export however — for example providing a your-library/foo im‐
port — you need to take additional care for providing type definitions. Unfortunately, TypeScript by default
will not resolve the types condition for an export like { "./foo": { "types": "./dist/foo.d.ts", ...
}} . Instead, it will search for a foo.d.ts relative to the root of your library (i.e. your-library/foo.d.ts
instead of your-library/dist/foo.d.ts ). To fix this, you have two options:
The first option is to require people using your library to set the moduleResolution option in their tscon‐
fig.json (or jsconfig.json ) to bundler (available since TypeScript 5, the best and recommended op‐
tion in the future), node16 or nodenext . This opts TypeScript into actually looking at the exports map and
resolving the types correctly.
The second option is to (ab)use the typesVersions feature from TypeScript to wire up the types. This is a
field inside package.json TypeScript uses to check for different type definitions depending on the
TypeScript version, and also contains a path mapping feature for that. We leverage that path mapping fea‐
ture to get what we want. For the mentioned foo export above, the corresponding typesVersions looks
like this:
{
"exports": {
"./foo": {
"types": "./dist/foo.d.ts",
"svelte": "./dist/foo.js"
}
},
"typesVersions": {
">4.0": {
"foo": ["./dist/foo.d.ts"]
}
}
}
>4.0 tells TypeScript to check the inner map if the used TypeScript version is greater than 4 (which should
in practice always be true). The inner map tells TypeScript that the typings for your-library/foo are
found within ./dist/foo.d.ts , which essentially replicates the exports condition. You also have * as a
112
Packaging
wildcard at your disposal to make many type definitions at once available without repeating yourself. Note
that if you opt into typesVersions you have to declare all type imports through it, including the root im‐
port (which is defined as "index.d.ts": [..] ).
Best practices
You should avoid using SvelteKit-specific modules like $app in your packages unless you intend for them to
only be consumable by other SvelteKit projects. E.g. rather than using import { browser } from
'$app/environment' you could use import { BROWSER } from 'esm-env' (see esm-env docs). You may
also wish to pass in things like the current URL or a navigation action as a prop rather than relying directly
on $app/stores , $app/navigation , etc. Writing your app in this more generic fashion will also make it
easier to setup tools for testing, UI demos and so on.
Ensure that you add aliases via svelte.config.js (not vite.config.js or tsconfig.json ), so that
they are processed by svelte-package .
You should think carefully about whether or not the changes you make to your package are a bug fix, a new
feature, or a breaking change, and update the package version accordingly. Note that if you remove any
paths from exports or any export conditions inside them from your existing library, that should be re‐
garded as a breaking change.
{
"exports": {
".": {
"types": "./dist/index.d.ts",
// changing `svelte` to `default` is a breaking change:
- "svelte": "./dist/index.js"
+ "default": "./dist/index.js"
},
// removing this is a breaking change:
- "./foo": {
- "types": "./dist/foo.d.ts",
- "svelte": "./dist/foo.js",
- "default": "./dist/foo.js"
- },
// adding this is ok:
+ "./bar": {
+ "types": "./dist/bar.d.ts",
+ "svelte": "./dist/bar.js",
+ "default": "./dist/bar.js"
+ }
}
}
Options
svelte-package accepts the following options:
-w / --watch — watch files in src/lib for changes and rebuild the package
-i / --input — the input directory which contains all the files of the package. Defaults to src/lib
113
sveltekit
-o / --o — the output directory where the processed files are written to. Your package.json 's ex‐
ports should point to files inside there, and the files array should include that folder. Defaults to
dist
-t / --types — whether or not to create type definitions ( d.ts files). We strongly recommend doing
this as it fosters ecosystem library quality. Defaults to true
Publishing
To publish the generated package:
npm publish
Caveats
All relative file imports need to be fully specified, adhering to Node's ESM algorithm. This means that for a
file like src/lib/something/index.js , you must include the filename with the extension:
If you are using TypeScript, you need to import .ts files the same way, but using a .js file ending, not a
.ts file ending. (This is a TypeScript design decision outside our control.) Setting "moduleResolution":
"NodeNext" in your tsconfig.json or jsconfig.json will help you with this.
All files except Svelte files (preprocessed) and TypeScript files (transpiled to JavaScript) are copied across
as-is.
Go to TOC
114
Accessibility
Accessibility
SvelteKit strives to provide an accessible platform for your app by default. Svelte's compile-time accessibili‐
ty checks will also apply to any SvelteKit application you build.
Here's how SvelteKit's built-in accessibility features work and what you need to do to help these features to
work as well as possible. Keep in mind that while SvelteKit provides an accessible foundation, you are still
responsible for making sure your application code is accessible. If you're new to accessibility, see the "fur‐
ther reading" section of this guide for additional resources.
We recognize that accessibility can be hard to get right. If you want to suggest improvements to how
SvelteKit handles accessibility, please open a GitHub issue.
Route announcements
In traditional server-rendered applications, every navigation (e.g. clicking on an <a> tag) triggers a full
page reload. When this happens, screen readers and other assistive technology will read out the new page's
title so that users understand that the page has changed.
Since navigation between pages in SvelteKit happens without reloading the page (known as client-side rout‐
ing), SvelteKit injects a live region onto the page that will read out the new page name after each naviga‐
tion. This determines the page name to announce by inspecting the <title> element.
Because of this behavior, every page in your app should have a unique, descriptive title. In SvelteKit, you
can do this by placing a <svelte:head> element on each page:
This will allow screen readers and other assistive technology to identify the new page after a navigation oc‐
curs. Providing a descriptive title is also important for SEO.
Focus management
In traditional server-rendered applications, every navigation will reset focus to the top of the page. This en‐
sures that people browsing the web with a keyboard or screen reader will start interacting with the page
from the beginning.
To simulate this behavior during client-side routing, SvelteKit focuses the <body> element after each navi‐
gation and enhanced form submission. There is one exception - if an element with the autofocus attribute
is present, SvelteKit will focus that element instead. Make sure to consider the implications for assistive
technology when using that attribute.
If you want to customize SvelteKit's focus management, you can use the afterNavigate hook:
115
sveltekit
afterNavigate(() => {
/** @type {HTMLElement | null} */
const to_focus = document.querySelector('.focus-me');
to_focus?.focus();
});
You can also programmatically navigate to a different page using the goto function. By default, this will
have the same client-side routing behavior as clicking on a link. However, goto also accepts a keepFocus
option that will preserve the currently-focused element instead of resetting focus. If you enable this option,
make sure the currently-focused element still exists on the page after navigation. If the element no longer
exists, the user's focus will be lost, making for a confusing experience for assistive technology users.
If your content is available in multiple languages, you should set the lang attribute based on the language
of the current page. You can do this with SvelteKit's handle hook:
Further reading
For the most part, building an accessible SvelteKit app is the same as building an accessible web app. You
should be able to apply information from the following general accessibility resources to any web experience
you build:
116
Accessibility
Go to TOC
117
sveltekit
SEO
The most important aspect of SEO is to create high-quality content that is widely linked to from around the
web. However, there are a few technical considerations for building sites that rank well.
SvelteKit's rendering is highly configurable and you can implement dynamic rendering if necessary.
It's not generally recommended, since SSR has other benefits beyond SEO.
Performance
Signals such as Core Web Vitals impact search engine ranking. Because Svelte and SvelteKit introduce mini‐
mal overhead, it's easier to build high performance sites. You can test your site's performance using
Google's PageSpeed Insights or Lighthouse.
Normalized URLs
SvelteKit redirects pathnames with trailing slashes to ones without (or vice versa depending on your config‐
uration), as duplicate URLs are bad for SEO.
Manual setup
<title> and <meta>
Every page should have well-written and unique <title> and <meta name="description"> elements in‐
side a <svelte:head> . Guidance on how to write descriptive titles and descriptions, along with other sug‐
gestions on making content understandable by search engines, can be found on Google's Lighthouse SEO
audits documentation.
A common pattern is to return SEO-related data from page load functions, then use it (as $page.d
ata ) in a <svelte:head> in your root layout.
118
SEO
Structured data
Structured data helps search engines understand the content of a page. If you're using structured data
alongside svelte-preprocess , you will need to explicitly preserve ld+json data (this may change in
future):
// @filename: index.js
// cut---
import preprocess from 'svelte-preprocess';
Sitemaps
Sitemaps help search engines prioritize pages within your site, particularly when you have a large amount
of content. You can create a sitemap dynamically using an endpoint:
119
sveltekit
AMP
An unfortunate reality of modern web development is that it is sometimes necessary to create an
Accelerated Mobile Pages (AMP) version of your site. In SvelteKit this can be done by setting the inline‐
StyleThreshold option...
<html amp>
...
...and transforming the HTML using transformPageChunk along with transform imported from @svelte‐
js/amp :
To prevent shipping any unused CSS as a result of transforming the page to amp, we can use dropcss :
120
SEO
if (done) {
let css = '';
const markup = amp
.transform(buffer)
⚡
.replace(' ', 'amp') // dropcss can't handle this character
.replace(/<style amp-custom([^>]*?)>([^]+?)<\/style>/, (match,
attributes, contents) => {
css = contents;
return `<style amp-custom${attributes}></style>`;
});
It's a good idea to use the handle hook to validate the transformed HTML using amphtml-validato
r , but only if you're prerendering pages since it's very slow.
Go to TOC
121
sveltekit
Configuration
Your project's configuration lives in a svelte.config.js file at the root of your project. As well as
SvelteKit, this config object is used by other tooling that integrates with Svelte such as editor extensions.
// @filename: index.js
// cut---
import adapter from '@sveltejs/adapter-auto';
TYPES: @sveltejs/kit#Config
The kit property configures SvelteKit, and can have the following properties:
EXPANDED_TYPES: @sveltejs/kit#KitConfig
Go to TOC
122
Command Line Interface
However SvelteKit includes its own CLI for initialising your project:
svelte-kit sync
svelte-kit sync creates the tsconfig.json and all generated types (which you can import as
./$types inside routing files) for your project. When you create a new project, it is listed as the prepare
script and will be run automatically as part of the npm lifecycle, so you should not ordinarily have to run this
command.
Go to TOC
123
sveltekit
Modules
SvelteKit makes a number of modules available to your application.
MODULES
Go to TOC
124
Types
Types
Public types
The following types can be imported from @sveltejs/kit :
TYPES: @sveltejs/kit
Private types
The following are referenced by the public types documented above, but cannot be imported directly:
Generated types
The RequestHandler and Load types both accept a Params argument allowing you to type the params
object. For example this endpoint expects foo , bar and baz params:
Needless to say, this is cumbersome to write out, and less portable (if you were to rename the [foo] di‐
rectory to [qux] , the type would no longer reflect reality).
To solve this problem, SvelteKit generates .d.ts files for each of your endpoints and pages:
type RouteParams = {
foo: string;
bar: string;
baz: string;
}
125
sveltekit
These files can be imported into your endpoints and pages as siblings, thanks to the rootDirs option in
your TypeScript configuration:
type RouteParams = {
foo: string;
bar: string;
baz: string;
}
// @filename: index.js
// @errors: 2355
// cut---
/** @type {import('./$types').PageServerLoad} */
export async function GET({ params }) {
// ...
}
type RouteParams = {
foo: string;
bar: string;
baz: string;
}
// @filename: index.js
// @errors: 2355
// cut---
/** @type {import('./$types').PageLoad} */
export async function load({ params, fetch }) {
// ...
}
For this to work, your own tsconfig.json or jsconfig.json should extend from the generated .s
velte-kit/tsconfig.json (where .svelte-kit is your outDir ):
{ "extends": "./.svelte-kit/tsconfig.json" }
126
Types
Default tsconfig.json
The generated .svelte-kit/tsconfig.json file contains a mixture of options. Some are generated pro‐
grammatically based on your project configuration, and should generally not be overridden without good
reason:
Others are required for SvelteKit to work properly, and should also be left untouched unless you know what
you're doing:
App
TYPES: App
127
sveltekit
Go to TOC
128
Frequently asked questions
SvelteKit can also be deployed to a wide spectrum of hosted architectures via adapters. In cases where SSR
is used (or server-side logic is added without prerendering), those functions will be adapted to the target
backend. Some examples include:
In order to support SSR, a JS backend — such as Node.js or Deno-based server, serverless function, or
edge function — is required.
It is also possible to write custom adapters or leverage community adapters to deploy SvelteKit to more
platforms such as specialized server environments, browser extensions, or native applications. See integra‐
tions for more examples and integrations.
129
sveltekit
pected behaviour and edge cases. But don't worry, you are still getting HMR with SvelteKit! If you'd like to
preserve local state you can use the @hmr:keep or @hmr:keep-all directives as documented on the
svelte-hmr page.
Here are a few things to keep in mind when checking if a library is packaged correctly:
exports takes precedence over the other entry point fields such as main and module . Adding an ex‐
ports field may not be backwards-compatible as it prevents deep imports.
ESM files should end with .mjs unless "type": "module" is set in which any case CommonJS files
should end with .cjs .
main should be defined if exports is not. It should be either a CommonJS or ESM file and adhere to
the previous bullet. If a module field is defined, it should refer to an ESM file.
Svelte components should be distributed as uncompiled .svelte files with any JS in the package writ‐
ten as ESM only. Custom script and style languages, like TypeScript and SCSS, should be preprocessed
as vanilla JS and CSS respectively. We recommend using svelte-package for packaging Svelte li‐
braries, which will do this for you.
Libraries work best in the browser with Vite when they distribute an ESM version, especially if they are de‐
pendencies of a Svelte component library. You may wish to suggest to library authors that they provide an
ESM version. However, CommonJS (CJS) dependencies should work as well since, by default, vite-plug‐
in-svelte will ask Vite to pre-bundle them using esbuild to convert them to ESM.
130
Frequently asked questions
If you are still encountering issues we recommend searching both the Vite issue tracker and the issue track‐
er of the library in question. Sometimes issues can be worked around by fiddling with the optimizeDeps or
ssr config values though we recommend this as only a short-term workaround in favor of fixing the library
in question.
if (browser) {
// client-only code here
}
You can also run code in onMount if you'd like to run it after the component has been first rendered to the
DOM:
// @filename: ambient.d.ts
// @lib: ES2015
declare module 'some-browser-only-library';
// @filename: index.js
// cut---
import { onMount } from 'svelte';
onMount(async () => {
const { method } = await import('some-browser-only-library');
method('hello world');
});
If the library you'd like to use is side-effect free you can also statically import it and it will be tree-shaken
out in the server-side build where onMount will be automatically replaced with a no-op:
131
sveltekit
// @filename: ambient.d.ts
// @lib: ES2015
declare module 'some-browser-only-library';
// @filename: index.js
// cut---
import { onMount } from 'svelte';
import { method } from 'some-browser-only-library';
onMount(() => {
method('hello world');
});
Otherwise, if the library has side effects and you'd still prefer to use static imports, check out vite-plugin-
iso-import to support the ?client import suffix. The import will be stripped out in SSR builds. However,
note that you will lose the ability to use VS Code Intellisense if you use this method.
// @filename: ambient.d.ts
// @lib: ES2015
declare module 'some-browser-only-library?client';
// @filename: index.js
// cut---
import { onMount } from 'svelte';
import { method } from 'some-browser-only-library?client';
onMount(() => {
method('hello world');
});
Another approach is to set up a proxy to bypass CORS headaches. In production, you would rewrite a path
like /api to the API server; for local development, use Vite's server.proxy option.
How to setup rewrites in production will depend on your deployment platform. If rewrites aren't an option,
you could alternatively add an API route:
(Note that you may also need to proxy POST / PATCH etc requests, and forward request.headers , de‐
pending on your needs.)
132
Frequently asked questions
// @errors: 2322
// @filename: ambient.d.ts
declare module '@sveltejs/kit/vite'; // TODO this feels unnecessary, why can't it
'see' the declarations?
// @filename: index.js
// cut---
import { sveltekit } from '@sveltejs/kit/vite';
See Vite's configureServer docs for more details including how to control ordering.
133
sveltekit
One of the more interesting features of Yarn Berry is the ability to have a single global cache for packages,
instead of having multiple copies for each project on the disk. However, setting enableGlobalCache to true
causes building to fail, so it is recommended to add the following to the .yarnrc.yml file:
nodeLinker: node-modules
This will cause packages to be downloaded into a local node_modules directory but avoids the above prob‐
lem and is your best bet for using version 3 of Yarn at this point in time.
Go to TOC
134
Integrations
Integrations
Preprocessors
Preprocessors transform your .svelte files before passing them to the compiler. For example, if your
.svelte file uses TypeScript and PostCSS, it must first be transformed into JavaScript and CSS so that the
Svelte compiler can handle it. There are many available preprocessors. The Svelte team maintains two offi‐
cial ones discussed below.
vitePreprocess
vite-plugin-svelte offers a vitePreprocess feature which utilizes Vite for preprocessing. It is capable
of handling the language flavors Vite handles: TypeScript, PostCSS, SCSS, Less, Stylus, and SugarSS. For
convenience, it is re-exported from the @sveltejs/kit/vite package. If you set your project up with
TypeScript it will be included by default:
// svelte.config.js
import { vitePreprocess } from '@sveltejs/kit/vite';
export default {
preprocess: [vitePreprocess()]
};
svelte-preprocess
svelte-preprocess has some additional functionality not found in vitePreprocess such as support for
Pug, Babel, and global styles. However, vitePreprocess may be faster and require less configuration, so it
is used by default. Note that CoffeeScript is not supported by SvelteKit.
You will need to install svelte-preprocess with npm install --save-dev svelte-preprocess and add
it to your svelte.config.js . After that, you will often need to install the corresponding library such as
npm install -D sass or npm install -D less .
Adders
Svelte Adders allow you to setup many different complex integrations like Tailwind, PostCSS, Storybook,
Firebase, GraphQL, mdsvex, and more with a single command. Please see sveltesociety.dev for a full listing
of templates, components, and tools available for use with Svelte and SvelteKit.
Integration FAQs
The SvelteKit FAQ has a how to do X with SvelteKit, which may be helpful if you still have questions.
Go to TOC
135
sveltekit
SvelteKit is the successor to Sapper and shares many elements of its design.
If you have an existing Sapper app that you plan to migrate to SvelteKit, there are a number of changes
you will need to make. You may find it helpful to view some examples while migrating.
package.json
type: "module"
Add "type": "module" to your package.json . You can do this step separately from the rest as part of an
incremental migration if you are using Sapper 0.29.3 or newer.
dependencies
Remove polka or express , if you're using one of those, and any middleware such as sirv or compres‐
sion .
devDependencies
Remove sapper from your devDependencies and replace it with @sveltejs/kit and whichever adapter
you plan to use (see next section).
scripts
Any scripts that reference sapper should be updated:
sapper build should become vite build using the Node adapter
sapper export should become vite build using the static adapter
sapper dev should become vite dev
node __sapper__/build should become node build
Project files
The bulk of your app, in src/routes , can be left where it is, but several project files will need to be moved
or updated.
Configuration
Your webpack.config.js or rollup.config.js should be replaced with a svelte.config.js , as docu‐
mented here. Svelte preprocessor options should be moved to config.preprocess .
136
Migrating from Sapper
You will need to add an adapter. sapper build is roughly equivalent to adapter-node while sapper ex‐
port is roughly equivalent to adapter-static, though you might prefer to use an adapter designed for the
platform you're deploying to.
If you were using plugins for filetypes that are not automatically handled by Vite, you will need to find Vite
equivalents and add them to the Vite config.
src/client.js
This file has no equivalent in SvelteKit. Any custom logic (beyond sapper.start(...) ) should be ex‐
pressed in your +layout.svelte file, inside an onMount callback.
src/server.js
When using adapter-node the equivalent is a custom server. Otherwise, this file has no direct equivalent,
since SvelteKit apps can run in serverless environments.
src/service-worker.js
Most imports from @sapper/service-worker have equivalents in $service-worker :
files is unchanged
routes has been removed
shell is now build
timestamp is now version
src/template.html
The src/template.html file should be renamed src/app.html .
src/node_modules
A common pattern in Sapper apps is to put your internal library in a directory inside src/node_modules .
This doesn't work with Vite, so we use src/lib instead.
137
sveltekit
Old New
routes/about/index.svelte routes/about/+page.svelte
routes/about.svelte routes/about/+page.svelte
Your custom error page component should be renamed from _error.svelte to +error.svelte . Any
_layout.svelte files should likewise be renamed +layout.svelte . Any other files are ignored.
Imports
The goto , prefetch and prefetchRoutes imports from @sapper/app should be replaced with goto ,
preloadData and preloadCode imports respectively from $app/navigation .
The stores import from @sapper/app should be replaced — see the Stores section below.
Any files you previously imported from directories in src/node_modules will need to be replaced with
$lib imports.
Preload
As before, pages and layouts can export a function that allows data to be loaded before rendering takes
place.
This function has been renamed from preload to load , it now lives in a +page.js (or +layout.js )
next to its +page.svelte (or +layout.svelte ), and its API has changed. Instead of two arguments —
page and session — there is a single event argument.
Stores
In Sapper, you would get references to provided stores like so:
// @filename: ambient.d.ts
declare module '@sapper/app';
// @filename: index.js
138
Migrating from Sapper
// cut---
import { stores } from '@sapper/app';
const { preloading, page, session } = stores();
The page store still exists; preloading has been replaced with a navigating store that contains from
and to properties. page now has url and params properties, but no path or query .
You access them differently in SvelteKit. stores is now getStores , but in most cases it is unnecessary
since you can import navigating , and page directly from $app/stores .
Routing
Regex routes are no longer supported. Instead, use advanced route matching.
Segments
Previously, layout components received a segment prop indicating the child segment. This has been re‐
moved; you should use the more flexible $page.url.pathname value to derive the segment you're inter‐
ested in.
URLs
In Sapper, all relative URLs were resolved against the base URL — usually / , unless the basepath option
was used — rather than against the current page.
This caused problems and is no longer the case in SvelteKit. Instead, relative URLs are resolved against the
current page (or the destination page, for fetch URLs in load functions) instead. In most cases, it's easi‐
er to use root-relative (i.e. starts with / ) URLs, since their meaning is not context-dependent.
<a> attributes
sapper:prefetch is now data-sveltekit-preload-data
sapper:noscroll is now data-sveltekit-noscroll
Endpoints
In Sapper, server routes received the req and res objects exposed by Node's http module (or the aug‐
mented versions provided by frameworks like Polka and Express).
SvelteKit is designed to be agnostic as to where the app is running — it could be running on a Node server,
but could equally be running on a serverless platform or in a Cloudflare Worker. For that reason, you no
longer interact directly with req and res . Your endpoints will need to be updated to match the new
signature.
To support this environment-agnostic behavior, fetch is now available in the global context, so you don't
need to import node-fetch , cross-fetch , or similar server-side fetch implementations in order to use it.
139
sveltekit
Integrations
See integrations for detailed information about integrations.
HTML minifier
Sapper includes html-minifier by default. SvelteKit does not include this, but you can add it as a prod
dependency and then use it through a hook:
// @filename: ambient.d.ts
/// <reference types="@sveltejs/kit" />
declare module 'html-minifier';
// @filename: index.js
// cut---
import { minify } from 'html-minifier';
import { building } from '$app/environment';
const minification_options = {
collapseBooleanAttributes: true,
collapseWhitespace: true,
conservativeCollapse: true,
decodeEntities: true,
html5: true,
ignoreCustomComments: [/^#/],
minifyCSS: true,
minifyJS: false,
removeAttributeQuotes: true,
removeComments: false, // some hydration code needs comments, so leave them in
removeOptionalTags: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
sortAttributes: true,
sortClassName: true
};
return resolve(event, {
transformPageChunk: ({ html, done }) => {
page += html;
if (done) {
return building ? minify(page, minification_options) : page;
}
}
});
}
Note that prerendering is false when using vite preview to test the production build of the site, so
to verify the results of minifying, you'll need to inspect the built HTML files directly.
Go to TOC
140
Additional resources
Additional resources
FAQs
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.
Examples
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-tem‐
plate 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.
Support
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 respect‐
ful 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.
Go to TOC
141
sveltekit
Glossary
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 documenta‐
tion above.
CSR
Client-side rendering (CSR) is the generation of the page contents in the web browser using JavaScript.
In SvelteKit, client-side rendering will be used by default, but you can turn off JavaScript with the csr =
false page option.
Hydration
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-ren‐
dered 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 lis‐
teners 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.
In SvelteKit, pages will be hydrated by default, but you can turn off JavaScript with the csr = false page
option.
Prerendering
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 hit‐
ting it directly must get the same content from the server, and the page must not contain actions. 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.
In SvelteKit, you can control prerendering with the prerender page option and prerender config in
svelte.config.js .
142
Glossary
Routing
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.
In SvelteKit, client-side routing will be used by default, but you can skip it with data-sveltekit-reload .
SPA
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 shortcom‐
ing 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.
SSG
Static Site Generation (SSG) is a term that refers to a site where every page is prerendered. 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 pur‐
pose-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 per‐
form SSR. Once generated, the site can be served from CDNs, leading to great "time to first byte" perfor‐
mance. This delivery model is often referred to as JAMstack.
In SvelteKit, you can do static site generation by using adapter-static or by configuring every page to be
prerendered using the prerender page option or prerender config in svelte.config.js .
SSR
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).
In SvelteKit, pages are server-side rendered by default. You can disable SSR with the ssr page option.
Go to TOC
143
sveltekit
Colophon
This book is created by using the following sources:
Sveltekit - English
GitHub source: sveltejs/kit/documentation
Created: 2023-08-25
Bash v5.2.2
Vivliostyle, https://vivliostyle.org/
By: @shinokada
Viewer: https://read-html-download-pdf.vercel.app/
GitHub repo: https://github.com/shinokada/markdown-docs-as-pdf
Viewer repo: https://github.com/shinokada/read-html-download-pdf
144