| All Svelte and SvelteKit doc contents are in one web page. |
| Note |
| Important |
| Caution |
| Warning |
Svelte Docs
Introduction
Overview
SvelteKit Docs
Getting started
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 components 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 helps you build web apps while following modern best practices and providing solutions to common development challenges. It offers everything from basic functionalities — like a router that updates your UI when a link is clicked — to more advanced capabilities. Its extensive list of features includes build optimizations to load only the minimal required code; offline support; preloading pages before user navigation; configurable rendering to handle different parts of your app on the server via SSR, in the browser through client-side rendering, or at build-time with prerendering; image optimization; and much more. Building an app with all the modern best practices is fiendishly complicated, but 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 lighting-fast and feature-rich development experience by leveraging Vite with a Svelte plugin to do Hot Module Replacement (HMR).
Creating a project
The easiest way to start building a SvelteKit app is to run npx sv create:
npx sv create my-app
cd my-app
npm i
npm run dev
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.
There are two basic concepts:
-
Each page of your app is a Svelte component.
-
You create pages by adding files to the
src/routesdirectory of your project. These will be server-rendered so that a user’s first visit to your app is as fast as possible, then a client-side app takes over.
Try editing the files to get a feel for how everything works.
Editor setup
We recommend using Visual Studio Code (aka VS Code) with the Svelte extension, but support also exists for numerous other editors.
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
│ └ service-worker.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 eslint.config.js and so on, if you chose those options when running npx sv create).
Project files
src
The src directory contains the meat of your project. Everything except src/routes and src/app.html is optional.
-
libcontains your library code (utilities and components), which can be imported via the$libalias, or packaged up for distribution using svelte-package-
servercontains your server-only library code. It can be imported by using the$lib/serveralias. SvelteKit will prevent you from importing these in client code.
-
-
paramscontains any param matchers your app needs. -
routescontains the routes of your application. You can also colocate other components that are only used within a single route here. -
app.htmlis 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. -
%sveltekit.body%— the markup for a rendered page. This should live inside a<div>or other element, rather than directly inside<body>, to prevent bugs caused by browser extensions injecting elements that are then destroyed by the hydration process. SvelteKit will warn you in development if this is not the case. -
%sveltekit.assets%— eitherpaths.assets, if specified, or a relative path topaths.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 variable, which must begin with thepublicPrefix(usuallyPUBLIC_). It will fallback to''if not matched.
-
-
error.htmlis 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.jscontains your client hooks. -
hooks.server.jscontains your server hooks. -
service-worker.jscontains 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 toggle 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 like in this directory.
package.json
You package.json file must include @svelte/kit, svelte and vite as devDependencies.
When you create a project with npx sv create, you’ll also notice that package.json includes "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 Svelte configuration.
tsconfig.json
This file (or jsconfig.json, if you prefer type-checked .js files over .ts files) configures TypeScript, if you added typechecking during npx sv create. Since SvelteKit relies on certain configuration being set a specific way, it generates its own .svelte-kit/tsconfig.json file which your own config extends.
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 (configurable as outDir). You can ignore its contents, and delete them at any time (they will be regenerated when you next dev or build).
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 developer elsewhere.
These APIs are available in all modern browsers and in many non-browser environments like Cloudflare Workers, Deno, and Vercel Functions. During development, and in adapters for Node-based environments (including AWS Lambda), they’re made available via polyfills where necessary (for now, that is — Node is rapidly adding support for more web standards).
In particular, you’ll get comfortable with the following:
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 credentials. (To make credentialled fetches in server-side code outside load, you must explicitly pass cookie 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 method 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.headers. For example, you can get the request.headers as shown below, and use the json convenience function to send modified response.headers:
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = ({ request }) => {
// log all headers
console.log(...request.headers);
// create a JSON Response using a header we received
return json({
// retrieve a specific header
userAgent: request.headers.get('user-agent')
}, {
// set a header on the response
headers: { 'x-custom-header': 'potato' }
});
};
FormData
When dealing with HTML native form submissions you’ll be working with FormData objects.
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const POST: RequestHandler = async (event) => {
const body = await event.request.formData();
// log all fields
console.log([...body]);
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.
URLSearchParams
Wherever you encounter a URL, you can access query parameters via url.searchParams, which is an instance of URLSearchParams:
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:
const uuid = crypto.randomUUID();
Core concepts
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:
-
src/routesis the root route. -
src/routes/aboutcreates an/aboutroute. -
src/routes/blog/[slug]creates a route with a parameter,slug, that can be used to load data dynamically when a user requests a page like/blog/hello-world.
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.
We’ll introduce these files in a moment in more detail, but here are a few simple rules to help you remember how SvelteKit’s routing works:
-
All files can run on the server.
-
All files run on the client except
+serverfiles. -
+layoutand+errorfiles apply to subdirectories as well as the directory they live in.
+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>Hello and welcome to my site!</h1>
<a href="/about">About my site</a>
<h1>About this site</h1>
<p>TODO...</p>
<a href="/">Home</a>
Pages can receive data from load functions via the data prop.
<script lang="ts">
import type { PageData } from './$types'
let { data }: { data: PageData } = $props()
</script>
<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.
|
+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:
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = ({ params }) => {
if (params.slug === 'hello-world') {
return {
title: 'Hello world!',
content: 'Welcome to our blog. Lorem ipsum dolor sit amet...'
};
}
error(404, 'Not found');
};
This function runs alongside +page.svelte, which means it runs on the server-side rendering and in the browser during client-side navigation. See load for full details of the API.
| Must SSR?? Only CSR?? |
As well as load, +page.js can export values that configure the page’s behaviour:
-
export const prerender = trueorfalseorauto -
export const ssr = trueorfalse -
export const csr = trueorfalse
You can find more information about these in page options.
+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.
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params }) => {
const post = await getPostFromDatabase(params.slug);
if (post) {
return post;
}
error(404, 'Not found');
};
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:
<script>
import { page } from '$app/stores';
</script>
<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. It 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 customise 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.
|
You can read more about error handling here.
+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…
<script>
let { children } = $props();
</script>
{@render children()}
…but we can add whatever markup, styles and behaviour we want. The only requirement is that the component includes a @render tag for the page content. For example, let’s add a nav bar:
<script>
let { children } = $props();
</script>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/settings">Settings</a>
</nav>
{@render children()}
If we create pages for /, /about and /settings…
<h1>Home</h1>
<h1>About</h1>
src/routes/settings/+page.svelte
<h1>Settings</h1>
…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 likes /settings/profile and /settings/notifications with a shared submenu (for a real-life example, see github.com/settings).
We can create a layout that only applies to pages below /settings (while inheriting the root layout with the top-level nav):
<script lang="ts">
import type { LayoutData } from './$types';
import type { Snippet } from 'svelte';
let { data, children }: { data: LayoutData, children: Snippet } = $props();
</script>
<h1>Settings</h1>
<div class="submenu">
{#each data.sections as section}
<a href="/settings/{section.slug}">{section.title}</a>
{/each}
</div>
{@render children()}
You can see how data is populated by looking at the +layout.js example in the next section just below.
By default, each layout inherits the layout above it. Sometimes that isn’t what you want — in this case, advanced 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.
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = () => {
return {
sections: [
{ slug: 'profile', title: 'Profile' },
{ slug: 'notifications', title: 'Notifications' }
]
};
};
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:
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
console.log(data.sections); // [{ slug: 'profile', title: 'Profile' }, ...]
</script>
Often, layout data is unchanged when navigating between pages. SvelteKit will intelligently rerun load functions when necessary.
|
+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 corresponding to HTTP verbs like GET, POST, PATCH, PUT, DELETE, OPTIONS, and HEAD that take a RequestEvent argument and return a Response object.
For example we could create an /api/random-number route with a GET handle:
import { error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = ({ url }) => {
const min = Number(url.searchParams.get('min') ?? '0');
const max = Number(url.searchParams.get('max') ?? '1');
const d = max - min;
if (isNaN(d) || d < 0) {
error(400, 'min and max must be numbers, and min must be less than max');
}
const random = min + Math.random() * d;
return new Response(String(random));
};
| To be completed |
$types
| To be completed |
Other files
| To be completed |
Further reading
| To be completed |
Build and deploy
Building your app
Building a SvelteKit app happens in two stages, which both happen when you run vite build (usually via npm run build).
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.
During the build
SvelteKit will load your +page/layout(.server).js files (and all files they import) for analysis during the build. Any code that should not be executed at this stage must check that building from $app/environment is false:
import { building } from '$app/environment';
import { setupMyDatabase } from '$lib/server/database';
if (!building) {
setupMyDatabase();
}
export function load() {
// ...
}
Preview your app
After building, you can view your production build locally with vite preview (via npm run preview). Note that this will run the app in Node, and so is not a perfect reproduction of your deployed app — adapter-specific adjustments like the platform object do not apply to previews.
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:
-
@sveltejs/adapter-cloudflarefor Cloudflare Pages -
@sveltejs/adapter-cloudflare-workersfor Cloudflare Workers -
@sveltejs/adapter-netlifyfor Netlify -
@sveltejs/adapter-nodefor Node servers -
@sveltejs/adapter-staticfor static site generation (SSG) -
@sveltejs/adapter-vercelfor Vercel
Additional community-provided adapters exist for other platforms.
Using adapters
Your adapter is specified in svelte.config.js:
import adapter from 'svelte-adapter-foo';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
// adapter options go here
})
}
};
export default config;
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 documentation to learn more.
Zero-config deployments
When you create a new SvelteKit project with npx sv create, 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.
Adding community adapters
You can add zero-config support for additional adapters by editing adapter.js and opening a pull request.
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 you 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 package.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 package.json.
Compressing responses
You will typically want to compress responses coming from the server. If you are already deploying your server behind a reverse proxy for SSL or load balancing, it typically results in better performance to also handle compression at that layer since Node.js is single-threaded.
However, if you’re building a custom server and do want to add a compression middleware there, note that we would recommend using @polka/compression since SvelteKit streams responses and the more popular compression package does not support streaming and may cause errors when used.
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…
npm install dotenv
…and invoke it before running the build app:
node -r dotenv/config build
If you use Node.js v20.6+, you can use the --env-file flag instead:
node --env-file=.env build
PORT, HOST and SOCKET_PATH
By default, the server will accept connections on 0.0.0.0 using port 3000. These can be customised with the PORT and HOST environment variables:
HOST=127.0.0.1 PORT=4000 node build
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.
SOCKET_PATH=/tmp/socket node build
ORIGIN, PROTOCOL_HEADER, HOST_HEADER, and PORT_HEADER
HTTP doesn’t give SvelteKit a reliable way to know the URL that is currently being requested. The simplest way to tell SvelteKit where the app is being served is to set the ORIGIN environment variable:
ORIGIN=https://my.site node build
# or e.g. for local previewing and testing
ORIGIN=http://localhost:3000 node build
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:
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build
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 you’re hosting your proxy on a non-standard port and your reverse proxy supports x-forwarded-port, you can also set PORT_HEADER=x-forwarded-port.
|
If adapter-node can’t correctly determine the URL of your deployment, you may experience this error when using form actions: Cross-site POST form submissions are forbidden.
ADDRESS_HEADER and XFF_DEPTH
The RequestEvent object passed to hooks and endpoints includes an event.getClientAddress() function that returns the client’s IP address. By default this is the connecting remoteAddress. If your server is behind one or more proxies (such as a load balancer), this value will contain the innermost proxy’s IP address rather than the client’s, so we need to specify an ADDRESS_HEADER to read the address from:
ADDRESS_HEADER=True-Client-IP node build
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 address. 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 connection and the first two proxies:
<client address>, <proxy 1 address>, <proxy 2 address>
Some guides will tell you to read the left-most address, but this leaves you vulnerable to spoofing:
<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>
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. The body size can also be specified with a unit suffix for kilobytes (K), megabytes (M), or gigabytes (G). For example, 512K or 1M. Defaults to 512kb. You can disable this option with a value of Infinity (0 in older versions of the adapter) and implement a custom check in handle if you need something more advanced.
SHUTDOWN_TIMEOUT
The number of seconds to wait before forcefully closing any remaining connections after receiving a SIGTERM or SIGINT. Defaults to 30. Internally the adapter calls closeAllConnections. See Graceful shutdown for more details.
IDLE_TIMEOUT
When using systemd socket activation, IDLE_TIMEOUT specifies the number of seconds after which the app is automatically put to sleep when receiving no requests. If not set, the app runs continuously. See Socket activation for more details.
Options
The adapter can be configured with various options:
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter({
// default options are shown
out: 'build',
precompress: true,
envPrefix: ''
})
}
};
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 true.
envPrefix
If you need to change the name of the environment variables used to configure the deployment (for example, 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
Graceful shutdown
By default adapter-node gracefully shuts down the HTTP server when a SIGTERM or SIGINT signal is received. It will:
-
reject new request (server.close)
-
wait for requests that have already been made but not received a response yet to finish and close connections once they become idle (
server.closeIdleConnections) -
and finally, close any remaining connections that are still active after SHUTDOWN_TIMEOUT seconds. (
server.closeAllConnections)
| If you want to customize this behaviour you can use a custom server. |
You can listen to the sveltekit:shutdown event which is emitted after the HTTP server has closed all connections. Unlike Node’s exit event, the sveltekit:shutdown event supports asynchronous operations and is always emitted when all connections are closed even if the server has dangling work such as open database connections.
process.on('sveltekit:shutdown', async (reason) => {
await jobs.stop();
await db.close();
});
The parameter reason has one of the following values:
-
SIGINT— shutdown was triggered by aSIGINTsignal -
SIGTERM— shutdown was triggered by aSIGTERMsignal -
IDLE— shutdown was triggered byIDLE_TIMEOUT
Socket activation
Most Linux operating systems today use a modern process manager call systemd to start the server and run and manage services. You can configure your server to allocate a socket and scale your app on demand. This is called socket activation. In this case, the OS will pass two environment variables to your app — LISTEN_PID and LISTEN_FDS. The adapter will then listen on file descriptor 3 which refers to a systemd socket unit that you will have to create.
You can still use envPrefix with systemd socket activation. LISTEN_PID and LISTEN_FDS are always read without a prefix.
|
To take advantage of socket activation follow these steps.
-
Run your app as a systemd service. It can either run directly on the host system or inside a container (using Docker or a systemd portable service for example). If you additionally pass an
IDLE_TIMEOUTenvironment variable to your app it will gracefully shutdown if there are no requests forIDLE_TIMEOUTseconds. systemd will automatically start your app again when new requests are coming in.
[Service]
Environment=NODE_ENV=production IDLE_TIMEOUT=60
ExecStart=/usr/bin/node /usr/bin/myapp/build
-
Create an accompanying socket unit. The adapter only accepts a single socket.
[Socket]
ListenStream=3000
[Install]
WantedBy=sockets.target
-
Make sure systemd has recognised both units by running
sudo systemctl daemon-reload. Then enable the socket on boot and start it immediately usingsudo systemctl enable --now myapp.socket. The app will then automatically start once the first request is made tolocalhost:3000.
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 and Polka (or even just the built-in http.createServer) and set up your own server:
import { handler } from './build/handler.js';
import express from 'express';
const app = express();
// add a route that lives separately from the SvelteKit app
app.get('/healthcheck', (req, res) => {
res.end('ok');
});
// let SvelteKit handle everything else, including serving prerendered pages and static assets
app.use(handler);
app.listen(3000, () => {
console.log('listening on port 3000');
});
Static site generation
To use SvelteKit as a static site generator (SSG), use adapter-static.
This will prerender you entire site as 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 prerender option.
Usage
Install with npm i -D @sveltejs/adapter-static, then add the adapter to your 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
})
}
};
…and add the prerender option to your root layout:
// This can be false if you're using a fallback (i.e. SPA mode)
export const prerender = 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 trailingSlash: 'always' in your root layout to create /a/index.html instead.
|
Zero-config support
Some platform 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:
export default {
kit: {
adapter: adapter({...})
}
};
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. Thiw will generate .br and .gz files.
strict
By default, adapter-static checks that either all pages and endpoints (if any) of your app were prerendered, 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, if your repo name is not equivalent to your-username.github.io, make sure to update config.kit.paths.base to match your repo name. This is because the site will be served from https://your-username.github.io/your-repo-name rather than from the root.
You’ll also want to generate a fallback 404.html page to replace the default 404 page shown by GitHub Pages.
A config for GitHub Pages might look like the following:
import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
fallback: '404.html'
}),
paths: {
base: process.argv.includes('dev') ? '' : process.env.BASE_PATH
}
}
};
export default config;
You can use GitHub actions to automatically deploy your site to GitHub Pages when you make a change. Here’s an example workflow:
name: Deploy to GitHub Pages
on:
push:
branches: 'main'
jobs:
build_site:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# 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@v3
# with:
# version: 8
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm install
- name: build
env:
BASE_PATH: '/${{ github.event.repository.name }}'
run: |
npm run build
- name: Upload Artifacts
uses: actions/upload-pages-artifact@v3
with:
# this should match the `pages` option in your adapter-static options
path: 'build/'
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@v4
If you’re not using GitHub actions to deploy your site (for example, you’re pushing the built site to itw own repo), add an empty .nojekyll file in your static directory to prevent Jekyll from interfering.
Single-page apps
You can turn any SvelteKit app, using any adapter, into a fully client-rendered single-page app (SPA) by disabling SSR at the root layout:
export const ssr = false;
| In most situations this is not recommended: it harms SEO, tends to slow down perceived performance, and makes your app inaccessible to users if JavaScript fails or is disabled (which happens more often than you probably think). |
If you don’t have any server-side logic (i.e. +page.server.js, +layout.server.js or +server.js files) you can use adpater-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:
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 you 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.
Note that the fallback page will always contain absolute asset paths (i.e. beginning with / rather than .) regardless of the value of paths.relative, since it is used to respond to requests for arbitrary paths.
|
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
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /200.html [L]
</IfModule>
Prerendering individual pages
If you want certain pages to be prerendered, you can re-enable ssr alongside prerender for just those parts of your app:
export const prerender = true;
export const ssr = true;
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 values specific to Cloudflare Workers are emulated during local development, type declarations are automatically applied, and the ability to set Cloudflare-specific options is provided.
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 apapter to your 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>']
},
platformProxy: {
configPath: 'wrangler.toml',
environment: undefined,
experimentalJsonConfig: false,
persist: false
}
})
}
};
Options
routes
Allows you to customise the _routes.json file generated by adapter-cloudflare.
-
includedefines routes that will invoke a function, and defaults to['/*'] -
excludedefines 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 yourstaticdirectory -
<prerendered>contains a list of prerendered pages -
<all>(the default) contains all of the above
-
You can have up to 100 include and exclude rules combined. Generally you can omit the routes options, 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', …].
platformProxy
Preferences for the emulated platform.env local bindings. See the getPlatformProxy Wrangler API documentation for a full list of options.
Deployment
Please follow the Get Started Guide for Cloudflare Pages to begin.
When configuring your project settings, you must use the following settings:
-
Framework preset — SvelteKit
-
Build command —
npm run buildorvite build -
Build output directory —
.svelte-kit/cloudflare
Runtime APIs
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, caches, and cf, meaning that you can access it in hooks and endpoints:
export async function POST({ request, platform }) {
const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x');
}
SvelteKit’s built-in $env module should be preferred for environment variables.
|
To include type declarations for your bindings, reference them in your src/app.d.ts:
declare global {
namespace App {
interface Platform {
env?: {
YOUR_KV_NAMESPACE: KVNamespace;
YOUR_DURABLE_OBJECT_NAMESPACE: DurableObjectNamespace;
};
}
}
}
export {};
Testing Locally
Cloudflare Workers specific values in the platform property are emulated during dev and preview modes. Local bindings are created based on the configuration in your wrangler.toml file and are used to populate platform.env during development and preview. Use the adapter config platformProxy option to change your preferences for the bindings.
For testing the build, you should use Wrangler version 3. Once you have built your site, run wrangler pages dev .svelte-kit/cloudflare.
Notes
Functions contained in the /functions directory at the project’s root will not be included in the deployment, 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 custom 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.
Accessing the file system
You can’t use fs in Cloudflare Workers — you must prerender the routes in question.
Appendix
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 documentation 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 server-rendered HTML. The components can then be initialized on the client with that data without having to call the same API endpoints again. Svelte will then check that the DOM is in the expected state and attach event listeners in a process called hydration. Once the components are fully hydrated, they can react to changes to their properties just like any newly created Svelte component.
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 hitting 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 fot that content as discussed above.
In SvelteKit, you can control prerendering with the the prerender page option and prerender config in svelte.config.js.
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 shortcoming described above. However, some applications are not greatly impacted by these shortcomings such as a complex business application behind a login where SEO would not be important and it is known that users will be accessing the application from a consistent computing environment.
In SvelteKit, you can build an SPA with adapter-static.
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 purpose-built SSGs, SvelteKit does nicely allow for mixing and matching different rendering types on different pages. One benefit of fully prerendering a site is that you do not need to maintain or pay for servers to perform SSR. Once generated, the site can be served from CDNs, leading to great "time to first byte" performance. This delivery model is often referred to as JAMstack.
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.