This is the developer documentation for SvelteKit. # Introduction ## Before we begin > [!NOTE] If you're new to Svelte or SvelteKit we recommend checking out the [interactive tutorial](/tutorial/kit). > > If you get stuck, reach out for help in the [Discord chatroom](https://svelte.dev/chat). ## What is SvelteKit? SvelteKit is a framework for rapidly developing robust, performant web applications using [Svelte](../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](faq#What-can-I-make-with-SvelteKit). ## 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](/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](glossary#Routing) that updates your UI when a link is clicked — to more advanced capabilities. Its extensive list of features includes [build optimizations](https://vitejs.dev/guide/features.html#build-optimizations) to load only the minimal required code; [offline support](service-workers); [preloading](link-options#data-sveltekit-preload-data) pages before user navigation; [configurable rendering](page-options) to handle different parts of your app on the server via [SSR](glossary#SSR), in the browser through [client-side rendering](glossary#CSR), or at build-time with [prerendering](glossary#Prerendering); [image optimization](images); 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 lightning-fast and feature-rich development experience by leveraging [Vite](https://vitejs.dev/) with a [Svelte plugin](https://github.com/sveltejs/vite-plugin-svelte) to do [Hot Module Replacement (HMR)](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/config.md#hot). # Creating a project The easiest way to start building a SvelteKit app is to run `npx sv create`: ```bash npx sv create my-app cd my-app npm install npm run dev ``` The first command will scaffold a new project in the `my-app` directory asking you if you'd like to set up some basic tooling such as TypeScript. See [integrations](./integrations) for pointers on setting up additional tooling. The subsequent commands will then install its dependencies and start a server on [localhost:5173](http://localhost:5173). There are two basic concepts: - Each page of your app is a [Svelte](../svelte) component - You create pages by adding files to the `src/routes` directory of your project. These will be server-rendered so that a user's first visit to your app is as fast as possible, then a client-side app takes over Try editing the files to get a feel for how everything works. ## Editor setup We recommend using [Visual Studio Code (aka VS Code)](https://code.visualstudio.com/download) with [the Svelte extension](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode), but [support also exists for numerous other editors](https://sveltesociety.dev/resources#editor-support). # Project structure A typical SvelteKit project looks like this: ```bash 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. - `lib` contains your library code (utilities and components), which can be imported via the [`$lib`]($lib) alias, or packaged up for distribution using [`svelte-package`](packaging) - `server` contains your server-only library code. It can be imported by using the [`$lib/server`](server-only-modules) alias. SvelteKit will prevent you from importing these in client code. - `params` contains any [param matchers](advanced-routing#Matching) your app needs - `routes` contains the [routes](routing) 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%` — `` and `

{data.title}

{@html data.content}
``` > [!LEGACY] > In Svelte 4, you'd use `export let data` instead > [!NOTE] SvelteKit uses `` elements to navigate between routes, rather than a framework-specific `` 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: ```js /// file: src/routes/blog/[slug]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load({ 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 during server-side rendering and in the browser during client-side navigation. See [`load`](load) for full details of the API. As well as `load`, `+page.js` can export values that configure the page's behaviour: - `export const prerender = true` or `false` or `'auto'` - `export const ssr = true` or `false` - `export const csr = true` or `false` You can find more information about these in [page options](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]($env-static-private) like API keys — then you can rename `+page.js` to `+page.server.js` and change the `PageLoad` type to `PageServerLoad`. ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare global { const getPostFromDatabase: (slug: string) => { title: string; content: string; } } export {}; // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageServerLoad} */ export async function load({ 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](https://github.com/rich-harris/devalue). See [`load`](load) for full details of the API. Like `+page.js`, `+page.server.js` can export [page options](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 `
` element. To learn how to use them, see the [form actions](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: ```svelte

{page.status}: {page.error.message}

``` > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. 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 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. > [!NOTE] `+error.svelte` is _not_ used when an error occurs inside [`handle`](hooks#Server-hooks-handle) or a [+server.js](#server) request handler. You can read more about error handling [here](errors). ## +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... ```svelte {@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: ```svelte
{@render children()} ``` If we create pages for `/`, `/about` and `/settings`... ```html /// file: src/routes/+page.svelte

Home

``` ```html /// file: src/routes/about/+page.svelte

About

``` ```html /// file: src/routes/settings/+page.svelte

Settings

``` ...the nav will always be visible, and clicking between the three pages will only result in the `

` 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 example, see [github.com/settings](https://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): ```svelte

Settings

{@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](advanced-routing#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`](load) function in `+layout.js`. ```js /// file: src/routes/settings/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { sections: [ { slug: 'profile', title: 'Profile' }, { slug: 'notifications', title: 'Notifications' } ] }; } ``` If a `+layout.js` exports [page options](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: ```svelte ``` > [!NOTE] Often, layout data is unchanged when navigating between pages. SvelteKit will intelligently rerun [`load`](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](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`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. For example we could create an `/api/random-number` route with a `GET` handler: ```js /// file: src/routes/api/random-number/+server.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export function GET({ 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)); } ``` The first argument to `Response` can be a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/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`](@sveltejs-kit#error), [`redirect`](@sveltejs-kit#redirect) and [`json`](@sveltejs-kit#json) methods from `@sveltejs/kit` for convenience (but you don't have to). If an error is thrown (either `error(...)` or an unexpected error), the response will be a JSON representation of the error or a fallback error page — which can be customised via `src/error.html` — depending on the `Accept` header. The [`+error.svelte`](#error) component will _not_ be rendered in this case. You can read more about error handling [here](errors). > [!NOTE] When creating an `OPTIONS` handler, note that Vite will inject `Access-Control-Allow-Origin` and `Access-Control-Allow-Methods` headers — these will not be present in production unless you add them. > [!NOTE] `+layout` files have no effect on `+server.js` files. If you want to run some logic before each request, add it to the server [`handle`](hooks#Server-hooks-handle) hook. ### Receiving data By exporting `POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`/`HEAD` handlers, `+server.js` files can be used to create a complete API: ```svelte + = {total} ``` ```js /// file: src/routes/api/add/+server.js import { json } from '@sveltejs/kit'; /** @type {import('./$types').RequestHandler} */ export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } ``` > [!NOTE] In general, [form actions](form-actions) are a better way to submit data from the browser to the server. > [!NOTE] If a `GET` handler is exported, a `HEAD` request will return the `content-length` of the `GET` handler's response body. ### Fallback method handler Exporting the `fallback` handler will match any unhandled request methods, including methods like `MOVE` which have no dedicated export from `+server.js`. ```js // @errors: 7031 /// file: src/routes/api/add/+server.js import { json, text } from '@sveltejs/kit'; export async function POST({ request }) { const { a, b } = await request.json(); return json(a + b); } // This handler will respond to PUT, PATCH, DELETE, etc. /** @type {import('./$types').RequestHandler} */ export async function fallback({ request }) { return text(`I caught your ${request.method} request!`); } ``` > [!NOTE] For `HEAD` requests, the `GET` handler takes precedence over the `fallback` handler. ### 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: - `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 annotations) to give you type safety when working with your root files. For example, annotating `let { data } = $props()` with `PageData` (or `LayoutData`, for a `+layout.svelte` file) tells TypeScript that the type of `data` is whatever was returned from `load`: ```svelte ``` In turn, annotating the `load` function with `PageLoad`, `PageServerLoad`, `LayoutLoad` or `LayoutServerLoad` (for `+page.js`, `+page.server.js`, `+layout.js` and `+layout.server.js` respectively) ensures that `params` and the return value are correctly typed. 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](/blog/zero-config-type-safety) 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`]($lib). ## Further reading - [Tutorial: Routing](/tutorial/kit/pages) - [Tutorial: API routes](/tutorial/kit/get-handlers) - [Docs: Advanced routing](advanced-routing) # Loading data Before a [`+page.svelte`](routing#page-page.svelte) component (and its containing [`+layout.svelte`](routing#layout-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: ```js /// file: src/routes/blog/[slug]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ params }) { return { post: { title: `Title for ${params.slug} goes here`, content: `Content for ${params.slug} goes here` } }; } ``` ```svelte

{data.post.title}

{@html data.post.content}
``` > [!LEGACY] > In Svelte 4, you'd use `export let data` instead Thanks to the generated `$types` module, we get full type safety. A `load` function in a `+page.js` file runs both on the server and in the browser (unless combined with `export const ssr = false`, in which case it will [only run in the browser](page-options#ssr)). If your `load` function should _always_ run on the server (because it uses private environment variables, for example, or accesses a database) 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: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { post: await db.getPost(params.slug) }; } ``` Notice that the type changed from `PageLoad` to `PageServerLoad`, because server `load` functions can access additional arguments. To understand when to use `+page.js` and when to use `+page.server.js`, see [Universal vs server](load#Universal-vs-server). ## Layout data Your `+layout.svelte` files can also load data, via `+layout.js` or `+layout.server.js`. ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { return { posts: await db.getPostSummaries() }; } ``` ```svelte
{@render children()}
``` 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. ```svelte /// file: src/routes/blog/[slug]/+page.svelte

{data.post.title}

{@html data.post.content}
+++{#if next}

Next post: {next.title}

{/if}+++ ``` > [!NOTE] 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 {page.data.title} ``` Type information for `page.data` is provided by `App.PageData`. > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. > It provides a `page` store with the same interface that you can subscribe to, e.g. `$page.data.title`. ## 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 * `+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. ### When does which load function run? Server `load` functions _always_ run on the server. 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](#Making-fetch-requests). All subsequent invocations of universal `load` functions happen in the browser. You can customize the behavior through [page options](page-options). If you disable [server side rendering](page-options#ssr), you'll get an SPA and universal `load` functions _always_ run on the client. If a route contains both universal and server `load` functions, the server `load` runs first. A `load` function is invoked at runtime, unless you [prerender](page-options#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`, `depends` and `untrack`). These are described 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](https://github.com/rich-harris/devalue) — anything that can be represented 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](#Streaming-with-promises), in which case it will be streamed to browsers. ### When to use which Server `load` functions are convenient when you need to access data directly from a database or filesystem, or need to use private environment variables. 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 component 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. When using both, the server `load` return value is _not_ passed directly to the page, but to the universal `load` function (as the `data` property): ```js /// file: src/routes/+page.server.js /** @type {import('./$types').PageServerLoad} */ export async function load() { return { serverMessage: 'hello from server load function' }; } ``` ```js /// file: src/routes/+page.js // @errors: 18047 /** @type {import('./$types').PageLoad} */ export async function load({ data }) { return { serverMessage: data.serverMessage, universalMessage: 'hello from universal load function' }; } ``` ## Using URL data Often the `load` function depends on the URL in one way or another. For this, the `load` function provides you with `url`, `route` and `params`. ### url An instance of [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL), containing properties like the `origin`, `hostname`, `pathname` and `searchParams` (which contains the parsed query string as a [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object). `url.hash` cannot be accessed during `load`, since it is unavailable on the server. > [!NOTE] In some environments this is derived from request headers during server-side rendering. If you're using [adapter-node](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`: ```js /// file: src/routes/a/[b]/[...c]/+page.js /** @type {import('./$types').PageLoad} */ export function load({ route }) { console.log(route.id); // '/a/[b]/[...c]' } ``` ### 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: ```json { "b": "x", "c": "y/z" } ``` ## Making fetch requests To get data from an external API or a `+server.js` handler, you can use the provided `fetch` function, which behaves identically to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) with a few additional features: - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` 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 hooking into the `text`, `json` and `arrayBuffer` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](hooks#Server-hooks-handle). - 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. ```js /// file: src/routes/items/[id]/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, params }) { const res = await fetch(`/api/items/${params.id}`); const item = await res.json(); return { item }; } ``` ## Cookies A server `load` function can get and set [`cookies`](@sveltejs-kit#Cookies). ```js /// file: src/routes/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getUser(sessionid: string | undefined): Promise<{ name: string, avatar: string }> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load({ cookies }) { const sessionid = cookies.get('sessionid'); return { user: await db.getUser(sessionid) }; } ``` 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. For example, if SvelteKit is serving my.domain.com: - domain.com WILL NOT receive cookies - my.domain.com WILL receive cookies - api.domain.com WILL NOT receive cookies - sub.my.domain.com WILL receive cookies 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](hooks#Server-hooks-handleFetch) to work around it. ## 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: ```js // @errors: 2322 1360 /// file: src/routes/products/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, setHeaders }) { const url = `https://cms.example.com/products.json`; const response = await fetch(url); // Headers are only set during SSR, caching the page's HTML // for the same length of time as the underlying data. setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') }); 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 using the `setHeaders` function. You cannot add a `set-cookie` header with `setHeaders` — use `cookies.set(name, value, options)` instead. ## Using parent data Occasionally it's useful for a `load` function to access data from a parent `load` function, which can be done with `await parent()`: ```js /// file: src/routes/+layout.js /** @type {import('./$types').LayoutLoad} */ export function load() { return { a: 1 }; } ``` ```js /// file: src/routes/abc/+layout.js /** @type {import('./$types').LayoutLoad} */ export async function load({ parent }) { const { a } = await parent(); return { b: a + 1 }; } ``` ```js /// file: src/routes/abc/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ parent }) { const { a, b } = await parent(); return { c: a + b }; } ``` ```svelte

{data.a} + {data.b} = {data.c}

``` > [!NOTE] Notice that the `load` function in `+page.js` receives the merged data from both layout `load` functions, not just the immediate parent. Inside `+page.server.js` and `+layout.server.js`, `parent` returns data from parent `+layout.server.js` files. In `+page.js` or `+layout.js` it will return data from parent `+layout.js` files. However, a missing `+layout.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. ```js /// file: +page.js // @filename: ambient.d.ts declare function getData(params: Record): Promise<{ meta: any }> // @filename: index.js // ---cut--- /** @type {import('./$types').PageLoad} */ export async function load({ params, parent }) { ---const parentData = await parent();--- const data = await getData(params); +++const parentData = await parent();+++ return { ...data, meta: { ...parentData.meta, ...data.meta } }; } ``` ## Errors If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For [_expected_](errors#Expected-errors) errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message: ```js /// file: src/routes/admin/+layout.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user?: { name: string; isAdmin: boolean; } } } // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { error(401, 'not logged in'); } if (!locals.user.isAdmin) { error(403, 'not an admin'); } } ``` Calling `error(...)` will throw an exception, making it easy to stop execution from inside helper functions. If an [_unexpected_](errors#Unexpected-errors) error is thrown, SvelteKit will invoke [`handleError`](hooks#Shared-hooks-handleError) and treat it as a 500 Internal Error. > [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the error yourself ## 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. Like `error(...)`, calling `redirect(...)` will throw an exception, making it easy to stop execution from inside helper functions. ```js /// file: src/routes/user/+layout.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user?: { name: string; } } } // @filename: index.js // ---cut--- import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ locals }) { if (!locals.user) { redirect(307, '/login'); } } ``` > [!NOTE] Don't use `redirect()` inside a `try {...}` block, as the redirect will immediately trigger the catch statement. In the browser, you can also navigate programmatically outside of a `load` function using [`goto`]($app-navigation#goto) from [`$app.navigation`]($app-navigation). > [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `redirect` yourself ## Streaming with promises When using a server `load`, promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare global { const loadPost: (slug: string) => Promise<{ title: string, content: string }>; const loadComments: (slug: string) => Promise<{ content: string }>; } export {}; // @filename: index.js // ---cut--- /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { // make sure the `await` happens at the end, otherwise we // can't start loading comments until we've loaded the post comments: loadComments(params.slug), post: await loadPost(params.slug) }; } ``` This is useful for creating skeleton loading states, for example: ```svelte

{data.post.title}

{@html data.post.content}
{#await data.comments} Loading comments... {:then comments} {#each comments as comment}

{comment.content}

{/each} {:catch error}

error loading comments: {error.message}

{/await} ``` When streaming data, be careful to handle promise rejections correctly. More specifically, the server could crash with an "unhandled promise rejection" error if a lazy-loaded promise fails before rendering starts (at which point it's caught) and isn't handling the error in some way. When using SvelteKit's `fetch` directly in the `load` function, SvelteKit will handle this case for you. For other promises, it is enough to attach a noop-`catch` to the promise to mark it as handled. ```js /// file: src/routes/+page.server.js /** @type {import('./$types').PageServerLoad} */ export function load({ fetch }) { const ok_manual = Promise.reject(); ok_manual.catch(() => {}); return { ok_manual, ok_fetch: fetch('/fetch/that/could/fail'), dangerous_unhandled: Promise.reject() }; } ``` > [!NOTE] On platforms that do not support streaming, such as AWS Lambda or Firebase, 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. > [!NOTE] Streaming data will only work when JavaScript is enabled. You should avoid returning 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. > [!NOTE] The headers and status code of a response cannot be changed once the response has started streaming, therefore you cannot `setHeaders` or throw redirects inside a streamed promise. > [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#Top-level-promises-are-no-longer-awaited) top-level promises were automatically awaited, only nested promises were streamed. ## Parallel loading When rendering (or navigating to) a page, SvelteKit runs all `load` functions concurrently, avoiding a waterfall 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. ## Rerunning load functions SvelteKit tracks the dependencies of each `load` function to avoid rerunning it unnecessarily during navigation. For example, given a pair of `load` functions like these... ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string }> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { return { post: await db.getPost(params.slug) }; } ``` ```js /// file: src/routes/blog/[slug]/+layout.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPostSummaries(): Promise> } // @filename: index.js // ---cut--- import * as db from '$lib/server/database'; /** @type {import('./$types').LayoutServerLoad} */ export async function load() { return { posts: await db.getPostSummaries() }; } ``` ...the one in `+page.server.js` will rerun if we navigate from `/blog/trying-the-raw-meat-diet` to `/blog/i-regret-my-choices` because `params.slug` has changed. The one in `+layout.server.js` will not, because the data is still valid. In other words, we won't call `db.getPostSummaries()` a second time. 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](#Streaming-with-promises) 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. Search parameters are tracked independently from the rest of the url. For example, accessing `event.url.searchParams.get("x")` inside a `load` function will make that `load` function re-run when navigating from `?x=1` to `?x=2`, but not when navigating from `?x=1&y=1` to `?x=1&y=2`. ### Untracking dependencies In rare cases, you may wish to exclude something from the dependency tracking mechanism. You can do this with the provided `untrack` function: ```js /// file: src/routes/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ untrack, url }) { // Untrack url.pathname so that path changes don't trigger a rerun if (untrack(() => url.pathname === '/')) { return { message: 'Welcome!' }; } } ``` ### Manual invalidation You can also rerun `load` functions that apply to the current page using [`invalidate(url)`]($app-navigation#invalidate), which reruns all `load` functions that depend on `url`, and [`invalidateAll()`]($app-navigation#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 custom identifier that starts with `[a-z]:`: ```js /// file: src/routes/random-number/+page.js /** @type {import('./$types').PageLoad} */ export async function load({ fetch, depends }) { // load reruns when `invalidate('https://api.example.com/random-number')` is called... const response = await fetch('https://api.example.com/random-number'); // ...or when `invalidate('app:random')` is called depends('app:random'); return { number: await response.json() }; } ``` ```svelte

random number: {data.number}

``` ### When do load functions rerun? To summarize, a `load` function will rerun in the following situations: - It references a property of `params` whose value has changed - It references a property of `url` (such as `url.pathname` or `url.search`) whose value has changed. Properties in `request.url` are _not_ tracked - It calls `url.searchParams.get(...)`, `url.searchParams.getAll(...)` or `url.searchParams.has(...)` and the parameter in question changes. Accessing other properties of `url.searchParams` will have the same effect as accessing `url.search`. - It calls `await parent()` and a parent `load` function reran - A child `load` function calls `await parent()` and is rerunning, and the parent is a server load function - It declared a dependency on a specific URL via [`fetch`](#Making-fetch-requests) (universal load only) or [`depends`](@sveltejs-kit#LoadEvent), and that URL was marked invalid with [`invalidate(url)`]($app-navigation#invalidate) - All active `load` functions were forcibly rerun with [`invalidateAll()`]($app-navigation#invalidateAll) `params` and `url` can change in response to a `` link click, a [`` interaction](form-actions#GET-vs-POST), a [`goto`]($app-navigation#goto) invocation, or a [`redirect`](@sveltejs-kit#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 preserved. If this isn't what you want, you can reset whatever you need to reset inside an [`afterNavigate`]($app-navigation#afterNavigate) callback, and/or wrap your component in a [`{#key ...}`](../svelte/key) block. ## Implications for authentication A couple features of loading data have important implications for auth checks: - Layout `load` functions do not run on every request, such as during client side navigation between child routes. [(When do load functions rerun?)](load#Rerunning-load-functions-When-do-load-functions-rerun) - Layout and page `load` functions run concurrently unless `await parent()` is called. If a layout `load` throws, the page `load` function runs, but the client will not receive the returned data. There are a few possible strategies to ensure an auth check occurs before protected code. To prevent data waterfalls and preserve layout `load` caches: - Use [hooks](hooks) to protect multiple routes before any `load` functions run - Use auth guards directly in `+page.server.js` `load` functions for route specific protection Putting an auth guard in `+layout.server.js` requires all child pages to call `await parent()` before protected code. Unless every child page depends on returned data from `await parent()`, the other options will be more performant. ## Further reading - [Tutorial: Loading data](/tutorial/kit/page-data) - [Tutorial: Errors and redirects](/tutorial/kit/error-basics) - [Tutorial: Advanced loading](/tutorial/kit/await-parent) # Form actions A `+page.server.js` file can export _actions_, which allow you to `POST` data to the server using the `` element. When using ``, 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: ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { default: async (event) => { // TODO log the user in } }; ``` To invoke this action from the `/login` page, just add a `` — no JavaScript needed: ```svelte ``` If someone were to click the button, the browser would send the form data via `POST` request to the server, running the default action. > [!NOTE] 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: ```html /// file: src/routes/+layout.svelte
``` ## Named actions Instead of one `default` action, a page can have as many named actions as it needs: ```js /// file: src/routes/login/+page.server.js /** @satisfies {import('./$types').Actions} */ export const actions = { --- default: async (event) => {--- +++ login: async (event) => {+++ // TODO log the user in }, +++ register: async (event) => { // TODO register the user }+++ }; ``` To invoke a named action, add a query parameter with the name prefixed by a `/` character: ```svelte
``` ```svelte ``` 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 ``: ```svelte /// file: src/routes/login/+page.svelte ++++++
``` > [!NOTE] 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. ## 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. ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- import * as db from '$lib/server/db'; /** @type {import('./$types').PageServerLoad} */ export async function load({ cookies }) { const user = await db.getUserFromSession(cookies.get('sessionid')); return { user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` ```svelte {#if form?.success}

Successfully logged in! Welcome back, {data.user.name}

{/if} ``` > [!LEGACY] > In Svelte 4, you'd use `export let data` and `export let form` instead to declare properties ### 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`: ```js /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- +++import { fail } from '@sveltejs/kit';+++ import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); +++ if (!email) { return fail(400, { email, missing: true }); }+++ const user = await db.getUser(email); +++ if (!user || user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); }+++ cookies.set('sessionid', await db.createSession(user), { path: '/' }); return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` > [!NOTE] Note that as a precaution, we only return the email back to the page — not the password. ```svelte /// file: src/routes/login/+page.svelte
+++ {#if form?.missing}

The email field is required

{/if} {#if form?.incorrect}

Invalid credentials!

{/if}+++
``` The returned data must be serializable as JSON. Beyond that, the structure is entirely up to you. For example, if you had multiple forms on the page, you could distinguish which `
` the returned `form` data referred to with an `id` property or similar. ### Redirects Redirects (and errors) work exactly the same as in [`load`](load#Redirects): ```js // @errors: 2345 /// file: src/routes/login/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/db'; // @filename: index.js // ---cut--- import { fail, +++redirect+++ } from '@sveltejs/kit'; import * as db from '$lib/server/db'; /** @satisfies {import('./$types').Actions} */ export const actions = { login: async ({ cookies, request, +++url+++ }) => { const data = await request.formData(); const email = data.get('email'); const password = data.get('password'); const user = await db.getUser(email); if (!user) { return fail(400, { email, missing: true }); } if (user.password !== db.hash(password)) { return fail(400, { email, incorrect: true }); } cookies.set('sessionid', await db.createSession(user), { path: '/' }); +++ if (url.searchParams.has('redirectTo')) { redirect(303, url.searchParams.get('redirectTo')); }+++ return { success: true }; }, register: async (event) => { // TODO register the user } }; ``` ## 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` functions 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 update `event.locals` when you set or delete the cookie in an action: ```js /// file: src/hooks.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user: { name: string; } | null } } // @filename: global.d.ts declare global { function getUser(sessionid: string | undefined): { name: string; }; } 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); } ``` ```js /// file: src/routes/account/+page.server.js // @filename: ambient.d.ts declare namespace App { interface Locals { user: { name: string; } | null } } // @filename: index.js // ---cut--- /** @type {import('./$types').PageServerLoad} */ export function load(event) { return { user: event.locals.user }; } /** @satisfies {import('./$types').Actions} */ export const actions = { logout: async (event) => { event.cookies.delete('sessionid', { path: '/' }); event.locals.user = null; } }; ``` ## Progressive enhancement In the preceding sections we built a `/login` action that [works without client-side JavaScript](https://kryogenix.org/code/browser/everyonehasjs.html) — not a `fetch` in sight. That's great, but when JavaScript _is_ available we can progressively enhance our form interactions to provide a better user experience. ### use:enhance The easiest way to progressively enhance a form is to add the `use:enhance` action: ```svelte /// file: src/routes/login/+page.svelte ``` > [!NOTE] `use:enhance` can only be used with forms that have `method="POST"`. It will not work with `method="GET"`, which is the default for forms without a specified method. Attempting to use `use:enhance` on forms without `method="POST"` will result in an error. > [!NOTE] Yes, it's a little confusing that the `enhance` action and `` 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. For example, if your form looks like ``, the `form` prop and the `page.form` state 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`](#Progressive-enhancement-Customising-use:enhance) - reset the `` element - 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](accessibility#Focus-management) to the appropriate element ### Customising use:enhance 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`. ```svelte { // `formElement` is this `` 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 return async ({ result, update }) => { // `result` is an `ActionResult` object // `update` is a function which triggers the default logic that would be triggered if this callback wasn't set }; }} > ``` You can use these functions to show and hide loading UI, and so on. If you return a callback, you may need to reproduce part of the default `use:enhance` behaviour, but without invalidating all data on a successful response. You can do so with `applyAction`: ```svelte /// file: src/routes/login/+page.svelte { return async ({ result }) => { // `result` is an `ActionResult` object +++ if (result.type === 'redirect') { goto(result.location); } else { await applyAction(result); }+++ }; }} > ``` The behaviour of `applyAction(result)` depends on `result.type`: - `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, { invalidateAll: true })` - `error` — renders the nearest `+error` boundary with `result.error` In all cases, [focus will be reset](accessibility#Focus-management). ### Custom event listener We can also implement progressive enhancement ourselves, without `use:enhance`, with a normal event listener on the ``: ```svelte
``` 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 default. To `POST` to an action in `+page.server.js` instead, use the custom `x-sveltekit-action` header: ```js const response = await fetch(this.action, { method: 'POST', body: data, +++ headers: { 'x-sveltekit-action': 'true' }+++ }); ``` ## 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`](routing#server) files to expose (for example) a JSON API. Here's how such an interaction could look like: ```svelte ``` ```js // @errors: 2355 1360 2322 /// file: src/routes/api/ci/+server.js /** @type {import('./$types').RequestHandler} */ export function POST() { // do something } ``` ## GET vs POST As we've seen, to invoke a form action you must use `method="POST"`. 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 `
` elements, using the client-side router instead of a full page navigation: ```html
``` Submitting this form will navigate to `/search?q=...` and invoke your load function but will not invoke an action. As with `
` elements, you can set the [`data-sveltekit-reload`](link-options#data-sveltekit-reload), [`data-sveltekit-replacestate`](link-options#data-sveltekit-replacestate), [`data-sveltekit-keepfocus`](link-options#data-sveltekit-keepfocus) and [`data-sveltekit-noscroll`](link-options#data-sveltekit-noscroll) attributes on the `
` to control the router's behaviour. ## Further reading - [Tutorial: Forms](/tutorial/kit/the-form-element) # Page options By default, SvelteKit will render (or [prerender](glossary#Prerendering)) 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**](glossary#Hydration). For this reason, you need to ensure that components can run in both places. SvelteKit will then initialize a [**router**](routing) that takes over subsequent navigations. You can control each of these on a page-by-page basis by exporting options from [`+page.js`](routing#page-page.js) or [`+page.server.js`](routing#page-page.server.js), or for groups of pages using a shared [`+layout.js`](routing#layout-layout.js) or [`+layout.server.js`](routing#layout-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_](glossary#Prerendering). ```js /// file: +page.js/+page.server.js/+server.js export const prerender = true; ``` Alternatively, you can set `export const prerender = true` in your root `+layout.js` or `+layout.server.js` and prerender everything except pages that are explicitly marked as _not_ prerenderable: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = false; ``` 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 recent/popular content but server-render the long tail) — for these cases, there's a third option, 'auto': ```js /// file: +page.js/+page.server.js/+server.js export const prerender = 'auto'; ``` > [!NOTE] If your entire app is suitable for prerendering, you can use [`adapter-static`](https://github.com/sveltejs/kit/tree/main/packages/adapter-static), which will output files suitable for use with any static webserver. The prerenderer will start at the root of your app and generate files for any prerenderable pages or `+server.js` routes it finds. Each page is scanned for `` 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.kit.prerender.entries`](configuration#prerender), or by exporting an [`entries`](#entries) function from your dynamic route. While prerendering, the value of `building` imported from [`$app/environment`]($app-environment) will be `true`. ### Prerendering server routes Unlike the other page options, `prerender` also applies to `+server.js` files. These files are _not_ affected by layouts, but will inherit default values from the pages that fetch data from them, if any. For example if a `+page.js` contains this `load` function... ```js /// file: +page.js export const prerender = true; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { const res = await fetch('/my-server-route.json'); return await res.json(); } ``` ...then `src/routes/my-server-route.json/+server.js` will be treated as prerenderable if it doesn't contain its own `export const prerender = false`. ### When not to prerender The basic rule is this: for a page to be prerenderable, any two users hitting it directly must get the same content from the server. > [!NOTE] 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`](load#Using-URL-data-url) 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](form-actions) cannot be prerendered, because a server must be able to handle the action `POST` requests. ### Route conflicts Because prerendering writes to the filesystem, it isn't possible to have two endpoints that would cause a directory and a file to have the same name. For example, `src/routes/foo/+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.json` and `foo/bar.json` files living harmoniously side-by-side. For _pages_, we skirt around this problem by writing `foo/index.html` instead of `foo`. ### Troubleshooting If you encounter an error like 'The following routes were marked as prerenderable, but were not prerendered' 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 reached by the prerendering crawler and thus wasn't prerendered. Since these routes cannot be dynamically server-rendered, this will cause errors when people try to access the route in question. There are a few ways to fix it: * Ensure that SvelteKit can find the route by following links from [`config.kit.prerender.entries`](configuration#prerender) or the [`entries`](#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. * Ensure that SvelteKit can find the route by discovering a link to it from one of your other prerendered pages that have server-side rendering enabled. * 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... ```bash / # non-dynamic /blog # non-dynamic /blog/[slug] # dynamic, because of `[slug]` ``` ...SvelteKit will prerender `/` and `/blog`, and in the process discover links like `` 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. This can be done with [`config.kit.prerender.entries`](configuration#prerender), or by exporting an `entries` function from a `+page.js`, a `+page.server.js` or a `+server.js` belonging to a dynamic route: ```js /// file: src/routes/blog/[slug]/+page.server.js /** @type {import('./$types').EntryGenerator} */ export function entries() { return [ { slug: 'hello-world' }, { slug: 'another-blog-post' } ]; } export const prerender = true; ``` `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 [hydrated](glossary#Hydration). If you set `ssr` to `false`, it renders an empty 'shell' page instead. This is useful if your page is unable 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](glossary#SSR)). ```js /// file: +page.js export const ssr = false; // If both `ssr` and `csr` are `false`, nothing will be rendered! ``` 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](glossary#Hydration) 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 category. In these cases you can disable CSR: ```js /// file: +page.js export const csr = false; // If both `csr` and `ssr` are `false`, nothing will be rendered! ``` Disabling CSR does not ship any JavaScript to the client. This means: * The webpage should work with HTML and CSS only. * ` ``` ```svelte

Welcome {user().name}

``` > [!NOTE] We're passing a function into `setContext` to keep reactivity across boundaries. Read more about it [here](https://svelte.dev/docs/svelte/$state#Passing-state-into-functions) > [!LEGACY] > You also use stores from `svelte/store` for this, but when using Svelte 5 it is recommended to make use of universal reactivity instead. Updating the value of context-based state in deeper-level pages or components while the page is being rendered via SSR will not affect the value in the parent component because it has already been rendered by the time the state value is updated. In contrast, on the client (when CSR is enabled, which is the default) the value will be propagated and components, pages, and layouts higher in the hierarchy will react to the new value. Therefore, 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. ## Component and page state is preserved When you navigate around your application, SvelteKit reuses existing layout and page components. For example, if you have a route like this... ```svelte

{data.title}

Reading time: {Math.round(estimatedReadingTime)} minutes

{@html data.content}
``` ...then navigating from `/blog/my-short-post` to `/blog/my-long-post` won't cause the layout, page and any other components within to be destroyed and recreated. Instead the `data` prop (and by extension `data.title` and `data.content`) will update (as it would with any other Svelte component) and, because the code isn't rerunning, lifecycle methods like `onMount` and `onDestroy` won't rerun and `estimatedReadingTime` won't be recalculated. Instead, we need to make the value [_reactive_](/tutorial/svelte/state): ```svelte /// file: src/routes/blog/[slug]/+page.svelte ``` > [!NOTE] If your code in `onMount` and `onDestroy` has to run again after navigation you can use [afterNavigate]($app-navigation#afterNavigate) and [beforeNavigate]($app-navigation#beforeNavigate) respectively. Reusing components like this means that things like sidebar scroll state are preserved, and you can easily animate between changing values. In the case that you do need to completely destroy and remount a component on navigation, you can use this pattern: ```svelte {#key page.url.pathname} {/key} ``` ## Storing state in the URL If you have state that should survive a reload and/or affect SSR, such as filters or sorting rules on a table, URL search parameters (like `?sort=price&order=ascending`) are a good place to put them. You can put them in `
` or `` attributes, or set them programmatically via `goto('?key=value')`. They can be accessed inside `load` functions via the `url` parameter, and inside components via `page.url.searchParams`. ## Storing ephemeral state in snapshots Some UI state, such as 'is the accordion open?', is disposable — if the user navigates away or refreshes the page, it doesn't matter if the state is lost. In some cases, you _do_ want the data to persist if the user navigates to a different page and comes back, but storing the state in the URL or in a database would be overkill. For this, SvelteKit provides [snapshots](snapshots), which let you associate component state with a history entry. # 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](page-options#prerender) 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`]($app-environment) is `false`: ```js +++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](adapters#Platform-specific-context) 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-cloudflare`](adapter-cloudflare) for Cloudflare Pages - [`@sveltejs/adapter-cloudflare-workers`](adapter-cloudflare-workers) for Cloudflare Workers - [`@sveltejs/adapter-netlify`](adapter-netlify) for Netlify - [`@sveltejs/adapter-node`](adapter-node) for Node servers - [`@sveltejs/adapter-static`](adapter-static) for static site generation (SSG) - [`@sveltejs/adapter-vercel`](adapter-vercel) for Vercel Additional [community-provided adapters](https://sveltesociety.dev/packages?category=sveltekit-adapters) exist for other platforms. ## Using adapters Your adapter is specified in `svelte.config.js`: ```js /// file: svelte.config.js // @filename: ambient.d.ts declare module 'svelte-adapter-foo' { const adapter: (opts: any) => import('@sveltejs/kit').Adapter; export default adapter; } // @filename: index.js // ---cut--- 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](hooks) and [server routes](routing#server) 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`](https://github.com/sveltejs/kit/tree/main/packages/adapter-auto) by default. This adapter automatically installs and uses the correct adapter for supported environments when you deploy: - [`@sveltejs/adapter-cloudflare`](adapter-cloudflare) for [Cloudflare Pages](https://developers.cloudflare.com/pages/) - [`@sveltejs/adapter-netlify`](adapter-netlify) for [Netlify](https://netlify.com/) - [`@sveltejs/adapter-vercel`](adapter-vercel) for [Vercel](https://vercel.com/) - [`svelte-adapter-azure-swa`](https://github.com/geoffrich/svelte-adapter-azure-swa) for [Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/) - [`svelte-kit-sst`](https://github.com/sst/sst/tree/master/packages/svelte-kit-sst) for [AWS via SST](https://sst.dev/docs/start/aws/svelte) - [`@sveltejs/adapter-node`](adapter-node) for [Google Cloud Run](https://cloud.google.com/run) 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`](adapter-vercel) and [`adapter-netlify`](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 [adapters.js](https://github.com/sveltejs/kit/blob/main/packages/adapter-auto/adapters.js) and opening a pull request. # Node servers To generate a standalone Node server, use [`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node). ## Usage Install with `npm i -D @sveltejs/adapter-node`, then add the adapter to your `svelte.config.js`: ```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 `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: ```bash node build ``` Development dependencies will be bundled into your app using [Rollup](https://rollupjs.org). 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](#Custom-server) and do want to add a compression middleware there, note that we would recommend using [`@polka/compression`](https://www.npmjs.com/package/@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](https://vitejs.dev/guide/env-and-mode.html#env-files).) In production, `.env` files are _not_ automatically loaded. To do so, install `dotenv` in your project... ```bash npm install dotenv ``` ...and invoke it before running the built app: ```bash node +++-r dotenv/config+++ build ``` If you use Node.js v20.6+, you can use the [`--env-file`](https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs) flag instead: ```bash 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 ``` > [!NOTE] [`x-forwarded-proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) and [`x-forwarded-host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/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](form-actions): > [!NOTE] Cross-site POST form submissions are forbidden ### `ADDRESS_HEADER` and `XFF_DEPTH` The [RequestEvent](@sveltejs-kit#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 ``` > [!NOTE] Headers can easily be spoofed. As with `PROTOCOL_HEADER` and `HOST_HEADER`, you should [know what you're doing](https://adam-p.ca/blog/2022/03/x-forwarded-for/) 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 connection and the first two proxies: ``` , , ``` Some guides will tell you to read the left-most address, but this leaves you [vulnerable to spoofing](https://adam-p.ca/blog/2022/03/x-forwarded-for/): ``` , , , ``` We instead read from the _right_, accounting for the number of trusted proxies. In this case, we would use `XFF_DEPTH=3`. > [!NOTE] 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`](hooks#Server-hooks-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` signal. Defaults to `30`. Internally the adapter calls [`closeAllConnections`](https://nodejs.org/api/http.html#servercloseallconnections). See [Graceful shutdown](#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](#Socket-activation) for more details. ## Options The adapter can be configured with various options: ```js // @errors: 2307 /// file: svelte.config.js 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: ```js envPrefix: 'MY_CUSTOM_'; ``` ```sh 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: 1. reject new requests ([`server.close`](https://nodejs.org/api/http.html#serverclosecallback)) 2. 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`](https://nodejs.org/api/http.html#servercloseidleconnections)) 3. and finally, close any remaining connections that are still active after [`SHUTDOWN_TIMEOUT`](#Environment-variables-SHUTDOWN_TIMEOUT) seconds. ([`server.closeAllConnections`](https://nodejs.org/api/http.html#servercloseallconnections)) > [!NOTE] If you want to customize this behaviour you can use a [custom server](#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. ```js // @errors: 2304 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 a `SIGINT` signal - `SIGTERM` - shutdown was triggered by a `SIGTERM` signal - `IDLE` - shutdown was triggered by [`IDLE_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) ## Socket activation Most Linux operating systems today use a modern process manager called systemd to start the server and run and manage services. You can configure your server to allocate a socket and start and scale your app on demand. This is called [socket activation](http://0pointer.de/blog/projects/socket-activated-containers.html). 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. > [!NOTE] You can still use [`envPrefix`](#Options-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. 1. Run your app as a [systemd service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html). 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_TIMEOUT`](#Environment-variables-IDLE_TIMEOUT) environment variable to your app it will gracefully shutdown if there are no requests for `IDLE_TIMEOUT` seconds. systemd will automatically start your app again when new requests are coming in. ```ini /// file: /etc/systemd/system/myapp.service [Service] Environment=NODE_ENV=production IDLE_TIMEOUT=60 ExecStart=/usr/bin/node /usr/bin/myapp/build ``` 2. Create an accompanying [socket unit](https://www.freedesktop.org/software/systemd/man/latest/systemd.socket.html). The adapter only accepts a single socket. ```ini /// file: /etc/systemd/system/myapp.socket [Socket] ListenStream=3000 [Install] WantedBy=sockets.target ``` 3. Make sure systemd has recognised both units by running `sudo systemctl daemon-reload`. Then enable the socket on boot and start it immediately using `sudo systemctl enable --now myapp.socket`. The app will then automatically start once the first request is made to `localhost: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](https://github.com/expressjs/express), [Connect](https://github.com/senchalabs/connect) or [Polka](https://github.com/lukeed/polka) (or even just the built-in [`http.createServer`](https://nodejs.org/dist/latest/docs/api/http.html#httpcreateserveroptions-requestlistener)) and set up your own server: ```js // @errors: 2307 7006 /// file: my-server.js 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`](https://github.com/sveltejs/kit/tree/main/packages/adapter-static). 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 `prerender` option](page-options#prerender). ## Usage Install with `npm i -D @sveltejs/adapter-static`, then add the adapter to your `svelte.config.js`: ```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 }) } }; ``` ...and add the [`prerender`](page-options#prerender) option to your root layout: ```js /// file: src/routes/+layout.js // This can be false if you're using a fallback (i.e. SPA mode) export const prerender = true; ``` > [!NOTE] You must ensure SvelteKit's [`trailingSlash`](page-options#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 platforms have zero-config support (more to come in future): - [Vercel](https://vercel.com) On these platforms, you should omit the adapter options so that `adapter-static` can provide the optimal configuration: ```js // @errors: 2304 /// file: svelte.config.js 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](single-page-apps), 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 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](https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages), if your repo name is not equivalent to `your-username.github.io`, make sure to update [`config.kit.paths.base`](configuration#paths) 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: ```js // @errors: 2307 2322 /// file: svelte.config.js 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: ```yaml ### file: .github/workflows/deploy.yml 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 its 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: ```js /// file: src/routes/+layout.js export const ssr = false; ``` > [!NOTE] 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](https://kryogenix.org/code/browser/everyonehasjs.html)). 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`](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: ```js // @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](https://surge.sh/help/adding-a-200-page-for-client-side-routing), 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] Note that the fallback page will always contain absolute asset paths (i.e. beginning with `/` rather than `.`) regardless of the value of [`paths.relative`](configuration#paths), since it is used to respond to requests for arbitrary paths. ## Apache To run an SPA on [Apache](https://httpd.apache.org/), you should add a `static/.htaccess` file to route requests to the fallback page: ``` RewriteEngine On RewriteBase / RewriteRule ^200\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /200.html [L] ``` ## 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: ```js /// file: src/routes/my-prerendered-page/+page.js export const prerender = true; export const ssr = true; ``` # Cloudflare Pages To deploy to [Cloudflare Pages](https://developers.cloudflare.com/pages/), use [`adapter-cloudflare`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare). This adapter will be installed by default when you use [`adapter-auto`](adapter-auto). If you plan on staying with Cloudflare Pages, you can switch from [`adapter-auto`](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](https://blog.cloudflare.com/cloudflare-pages-goes-full-stack/) - `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.config.js`: ```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: [''] }, platformProxy: { configPath: 'wrangler.toml', environment: undefined, experimentalJsonConfig: false, persist: false } }) } }; ``` ## Options ### routes Allows you to customise the [`_routes.json`](https://developers.cloudflare.com/pages/platform/functions/routing/#create-a-_routesjson-file) 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: - `` contains your app's build artifacts (the files generated by Vite) - `` contains the contents of your `static` directory - `` contains a list of prerendered pages - `` (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 `` 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](https://developers.cloudflare.com/workers/wrangler/api/#syntax) Wrangler API documentation for a full list of options. ## Deployment Please follow the [Get Started Guide](https://developers.cloudflare.com/pages/get-started) for Cloudflare Pages to begin. When configuring your project settings, you must use the following settings: - **Framework preset** – SvelteKit - **Build command** – `npm run build` or `vite build` - **Build output directory** – `.svelte-kit/cloudflare` ## Runtime APIs The [`env`](https://developers.cloudflare.com/workers/runtime-apis/fetch-event#parameters) object contains your project's [bindings](https://developers.cloudflare.com/pages/platform/functions/bindings/), which consist of KV/DO namespaces, etc. It is passed to SvelteKit via the `platform` property, along with [`context`](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#contextwaituntil), [`caches`](https://developers.cloudflare.com/workers/runtime-apis/cache/), and [`cf`](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), meaning that you can access it in hooks and endpoints: ```js // @errors: 7031 export async function POST({ request, platform }) { const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x'); } ``` > [!NOTE] 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`: ```ts /// file: src/app.d.ts import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types'; 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](https://developers.cloudflare.com/workers/wrangler/configuration/#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](#Options-platformProxy) to change your preferences for the bindings. For testing the build, you should use [wrangler](https://developers.cloudflare.com/workers/cli-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](https://developers.cloudflare.com/pages/platform/functions/#advanced-mode). Functions should be implemented as [server endpoints](routing#server) 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](routing#server) or with the [`handle`](hooks#Server-hooks-handle) hook. ## Troubleshooting ### Further reading You may wish to refer to [Cloudflare's documentation for deploying a SvelteKit site](https://developers.cloudflare.com/pages/framework-guides/deploy-a-svelte-site). ### Accessing the file system You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#prerender) the routes in question. # Cloudflare Workers To deploy to [Cloudflare Workers](https://workers.cloudflare.com/), use [`adapter-cloudflare-workers`](https://github.com/sveltejs/kit/tree/main/packages/adapter-cloudflare-workers). > [!NOTE] 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, instant rollback and so on. ## Usage Install with `npm i -D @sveltejs/adapter-cloudflare-workers`, then add the adapter to your `svelte.config.js`: ```js // @errors: 2307 /// file: svelte.config.js import adapter from '@sveltejs/adapter-cloudflare-workers'; export default { kit: { adapter: adapter({ config: 'wrangler.toml', platformProxy: { configPath: 'wrangler.toml', environment: undefined, experimentalJsonConfig: false, persist: false } }) } }; ``` ## Options ### config Path to your custom `wrangler.toml` or `wrangler.json` config file. ### platformProxy Preferences for the emulated `platform.env` local bindings. See the [getPlatformProxy](https://developers.cloudflare.com/workers/wrangler/api/#syntax) Wrangler API documentation for a full list of options. ## Basic Configuration This adapter expects to find a [wrangler.toml/wrangler.json](https://developers.cloudflare.com/workers/platform/sites/configuration) file in the project root. It should look something like this: ```toml /// file: wrangler.toml name = "" account_id = "" main = "./.cloudflare/worker.js" site.bucket = "./.cloudflare/public" build.command = "npm run build" compatibility_date = "2021-11-12" workers_dev = true ``` `` can be anything. `` can be found by logging into your [Cloudflare dashboard](https://dash.cloudflare.com) and grabbing it from the end of the URL: ``` https://dash.cloudflare.com/ ``` > [!NOTE] You should add the `.cloudflare` directory (or whichever directories you specified for `main` and `site.bucket`) to your `.gitignore`. You will need to install [wrangler](https://developers.cloudflare.com/workers/wrangler/get-started/) and log in, if you haven't already: ``` npm i -g wrangler wrangler login ``` Then, you can build your app and deploy it: ```sh wrangler deploy ``` ## Custom config If you would like to use a config file other than `wrangler.toml` you can specify so using the [`config` option](#Options-config). If you would like to enable [Node.js compatibility](https://developers.cloudflare.com/workers/runtime-apis/nodejs/#enable-nodejs-from-the-cloudflare-dashboard), you can add "nodejs_compat" flag to `wrangler.toml`: ```toml /// file: wrangler.toml compatibility_flags = [ "nodejs_compat" ] ``` ## Runtime APIs The [`env`](https://developers.cloudflare.com/workers/runtime-apis/fetch-event#parameters) object contains your project's [bindings](https://developers.cloudflare.com/pages/platform/functions/bindings/), which consist of KV/DO namespaces, etc. It is passed to SvelteKit via the `platform` property, along with [`context`](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/#contextwaituntil), [`caches`](https://developers.cloudflare.com/workers/runtime-apis/cache/), and [`cf`](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), meaning that you can access it in hooks and endpoints: ```js // @errors: 7031 export async function POST({ request, platform }) { const x = platform.env.YOUR_DURABLE_OBJECT_NAMESPACE.idFromName('x'); } ``` > [!NOTE] 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`: ```ts /// file: src/app.d.ts import { KVNamespace, DurableObjectNamespace } from '@cloudflare/workers-types'; 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](https://developers.cloudflare.com/workers/wrangler/configuration/#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](#Options-platformProxy) to change your preferences for the bindings. For testing the build, you should use [wrangler](https://developers.cloudflare.com/workers/cli-wrangler) **version 3**. Once you have built your site, run `wrangler dev`. ## 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](https://developers.cloudflare.com/workers/platform/limits/#worker-size) 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 worker by only importing such libraries on the client side. See [the FAQ](./faq#How-do-I-use-X-with-SvelteKit-How-do-I-use-a-client-side-only-library-that-depends-on-document-or-window) for more information. ### Accessing the file system You can't use `fs` in Cloudflare Workers — you must [prerender](page-options#prerender) the routes in question. # Netlify To deploy to Netlify, use [`adapter-netlify`](https://github.com/sveltejs/kit/tree/main/packages/adapter-netlify). This adapter will be installed by default when you use [`adapter-auto`](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`: ```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, // if true, will split your app into multiple functions // instead of creating a single one for the entire app. // if `edge` is true, this option cannot be used split: false }) } }; ``` Then, make sure you have a [netlify.toml](https://docs.netlify.com/configure-builds/file-based-configuration) file in the project root. This will determine where to write static assets based on the `build.publish` settings, as per this sample configuration: ```toml [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 the current Node LTS version 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](https://docs.netlify.com/configure-builds/manage-dependencies/#node-js-and-javascript) for details on manually specifying a current Node version. ## Netlify Edge Functions SvelteKit supports [Netlify Edge Functions](https://docs.netlify.com/netlify-labs/experimental-features/edge-functions/). If you pass the option `edge: true` to the `adapter` function, server-side rendering will happen in a Deno-based edge function that's deployed close to the site visitor. If set to `false` (the default), the site will deploy to Node-based Netlify Functions. ```js // @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 }) } }; ``` ## Netlify alternatives to SvelteKit functionality You may build your app using functionality provided directly by SvelteKit without relying on any Netlify functionality. Using the SvelteKit versions of these features will allow them to be used in dev mode, tested with integration tests, and to work with other adapters should you ever decide to switch away from Netlify. However, in some scenarios you may find it beneficial to use the Netlify versions of these features. One example would be if you're migrating an app that's already hosted on Netlify to SvelteKit. ### 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](https://docs.netlify.com/routing/redirects/#rule-processing-order). So always put your rules in the [`_redirects` file](https://docs.netlify.com/routing/redirects/#syntax-for-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](https://docs.netlify.com/routing/redirects/#rule-processing-order). ### Netlify Forms 1. Create your Netlify HTML form as described [here](https://docs.netlify.com/forms/setup/#html-forms), e.g. as `/routes/contact/+page.svelte`. (Don't forget 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](page-options#prerender) 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](https://docs.netlify.com/forms/setup/#success-messages) like `` then ensure the corresponding `/routes/success/+page.svelte` exists and is prerendered. ### Netlify Functions With this adapter, SvelteKit endpoints are hosted as [Netlify Functions](https://docs.netlify.com/functions/overview/). Netlify function handlers have additional context, including [Netlify Identity](https://docs.netlify.com/visitor-access/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](https://docs.netlify.com/functions/overview/) when the `edge` property is `false` in the adapter config or [edge functions](https://docs.netlify.com/edge-functions/overview/#app) when it is `true`. ```js // @errors: 2705 7006 /// file: +page.server.js export const load = async (event) => { const context = event.platform.context; console.log(context); // shows up in your functions log in the Netlify app }; ``` Additionally, you can add your own Netlify functions by creating a directory for them and adding the configuration to your `netlify.toml` file. For example: ```toml [build] command = "npm run build" publish = "build" [functions] directory = "functions" ``` ## Troubleshooting ### Accessing the file system You can't use `fs` in edge deployments. You _can_ use it in serverless deployments, but it won't work as expected, since files are not copied from your project into your deployment. Instead, use the [`read`]($app-server#read) function from `$app/server` to access your files. `read` does not work inside edge deployments (this may change in future). Alternatively, you can [prerender](page-options#prerender) the routes in question. # Vercel To deploy to Vercel, use [`adapter-vercel`](https://github.com/sveltejs/kit/tree/main/packages/adapter-vercel). This adapter will be installed by default when you use [`adapter-auto`](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`: ```js // @errors: 2307 2345 /// file: svelte.config.js import adapter from '@sveltejs/adapter-vercel'; export default { kit: { adapter: adapter({ // see below for options that can be set here }) } }; ``` ## 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`](page-options#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](https://vercel.com/docs/concepts/functions/edge-functions)... ```js /// file: about/+page.js /** @type {import('@sveltejs/adapter-vercel').Config} */ export const config = { runtime: 'edge' }; ``` ...and others as [Serverless Functions](https://vercel.com/docs/concepts/functions/serverless-functions) (note that by specifying `config` inside a layout, it applies to all child pages): ```js /// file: admin/+layout.js /** @type {import('@sveltejs/adapter-vercel').Config} */ export const config = { runtime: 'nodejs22.x' }; ``` The following options apply to all functions: - `runtime`: `'edge'`, `'nodejs18.x'`, `'nodejs20.x'` or `'nodejs22.x'`. By default, the adapter will select the `'nodejs.x'` corresponding to the Node version your project is configured to use on the Vercel dashboard - `regions`: an array of [edge network regions](https://vercel.com/docs/concepts/edge-network/regions) (defaulting to `["iad1"]` for serverless functions) or `'all'` if `runtime` is `edge` (its default). Note that multiple regions for serverless functions are only supported on Enterprise plans - `split`: if `true`, causes a route to be deployed as an individual function. If `split` is set to `true` at the adapter level, all routes will be deployed as individual functions Additionally, the following option applies to edge functions: - `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 And the following option apply to serverless functions: - `memory`: the amount of memory available to the function. Defaults to `1024` Mb, and can be decreased to `128` Mb or [increased](https://vercel.com/docs/concepts/limits/overview#serverless-function-memory) in 64Mb increments up to `3008` Mb on Pro or Enterprise accounts - `maxDuration`: [maximum execution duration](https://vercel.com/docs/functions/runtimes#max-duration) of the function. Defaults to `10` seconds for Hobby accounts, `15` 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. ## Image Optimization You may set the `images` config to control how Vercel builds your images. See the [image configuration reference](https://vercel.com/docs/build-output-api/v3/configuration#images) for full details. As an example, you may set: ```js // @errors: 2300 2842 7031 1181 1005 1136 1128 /// file: svelte.config.js import adapter from '@sveltejs/adapter-vercel'; export default { kit: { adapter({ images: { sizes: [640, 828, 1200, 1920, 3840], formats: ['image/avif', 'image/webp'], minimumCacheTTL: 300, domains: ['example-app.vercel.app'], } }) } }; ``` ## Incremental Static Regeneration Vercel supports [Incremental Static Regeneration](https://vercel.com/docs/incremental-static-regeneration) (ISR), which provides the performance and cost advantages of prerendered content with the flexibility of dynamically rendered content. To add ISR to a route, include the `isr` property in your `config` object: ```js // @errors: 2664 import { BYPASS_TOKEN } from '$env/static/private'; export const config = { isr: { // Expiration time (in seconds) before the cached asset will be re-generated by invoking the Serverless Function. // Setting the value to `false` means it will never expire. expiration: 60, // 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= cookie. // // Making a `GET` or `HEAD` request with `x-prerender-revalidate: ` will force the asset to be re-validated. bypassToken: BYPASS_TOKEN, // List of valid query parameters. Other parameters (such as utm tracking codes) will be ignored, // ensuring that they do not result in content being regenerated unnecessarily allowQuery: ['search'] } }; ``` The `expiration` property is required; all others are optional. > Pages that are [prerendered](page-options#prerender) will ignore ISR configuration. ## Environment variables Vercel makes a set of [deployment-specific environment variables](https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables) available. Like other environment variables, 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: ```js // @errors: 2305 /// file: +layout.server.js import { VERCEL_COMMIT_REF } from '$env/static/private'; /** @type {import('./$types').LayoutServerLoad} */ export function load() { return { deploymentGitBranch: VERCEL_COMMIT_REF }; } ``` ```svelte

This staging environment was deployed from {data.deploymentGitBranch}.

``` 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 optimisations like dead code elimination — rather than `$env/dynamic/private`. ## Skew protection When a new version of your app is deployed, assets belonging to the previous version may no longer be accessible. If a user is actively using your app when this happens, it can cause errors when they navigate — this is known as _version skew_. SvelteKit mitigates this by detecting errors resulting from version skew and causing a hard reload to get the latest version of the app, but this will cause any client-side state to be lost. (You can also proactively mitigate it by observing the [`updated`]($app-stores#updated) store value, which tells clients when a new version has been deployed.) [Skew protection](https://vercel.com/docs/deployments/skew-protection) is a Vercel feature that routes client requests to their original deployment. When a user visits your app, a cookie is set with the deployment ID, and any subsequent requests will be routed to that deployment for as long as skew protection is active. When they reload the page, they will get the newest deployment. (The `updated` store is exempted from this behaviour, and so will continue to report new deployments.) To enable it, visit the Advanced section of your project settings on Vercel. Cookie-based skew protection comes with one caveat: if a user has multiple versions of your app open in multiple tabs, requests from older versions will be routed to the newer one, meaning they will fall back to SvelteKit's built-in skew protection. ## 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](routing#server) 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. ### Node version Projects created before a certain date may default to using an older Node version than what SvelteKit currently requires. You can [change the Node version in your project settings](https://vercel.com/docs/concepts/functions/serverless-functions/runtimes/node-js#node.js-version). ## Troubleshooting ### Accessing the file system You can't use `fs` in edge functions. You _can_ use it in serverless functions, but it won't work as expected, since files are not copied from your project into your deployment. Instead, use the [`read`]($app-server#read) function from `$app/server` to access your files. `read` does not work inside routes deployed as edge functions (this may change in future). Alternatively, you can [prerender](page-options#prerender) the routes in question. # 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](https://github.com/sveltejs/kit/tree/main/packages) to a platform similar to yours and copying it as a starting point. Adapter packages implement the following API, which creates an `Adapter`: ```js // @errors: 2322 // @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 }, async emulate() { return { async platform({ config, prerender }) { // the returned object becomes `event.platform` during dev, build and // preview. Its shape is that of `App.Platform` } } }, supports: { read: ({ config, route }) => { // Return `true` if the route with the given `config` can use `read` // from `$app/server` in production, return `false` if it can't. // Or throw a descriptive error describing how to configure the deployment } } }; return adapter; } ``` Of these, `name` and `adapt` are required. `emulate` and `supports` are optional. Within the `adapt` method, there are a number of things that an adapter should do: - Clear out the build directory - Write SvelteKit output with `builder.writeClient`, `builder.writeServer`, and `builder.writePrerendered` - Output code that: - Imports `Server` from `${builder.getServerDirectory()}/index.js` - Instantiates the app with a manifest generated with `builder.generateManifest({ relativePath })` - Listens for requests from the platform, converts them to a standard [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) if necessary, calls the `server.respond(request, { getClientAddress })` function to generate a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) and responds with it - expose any platform-specific information to SvelteKit via the `platform` option passed to `server.respond` - Globally shims `fetch` to work on the target platform, if necessary. SvelteKit provides a `@sveltejs/kit/node/polyfills` helper for platforms that can use `undici` - Bundle the output to avoid needing to install dependencies on the target platform, if necessary - Put the user's static files and the generated JS/CSS in the correct location for the target platform Where possible, we recommend putting the adapter output under the `build/` directory with any intermediate output placed under `.svelte-kit/[adapter-name]`. # 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... ```bash /[org]/[repo]/tree/[branch]/[...file] ``` ...in which case a request for `/sveltejs/kit/tree/main/documentation/docs/04-advanced-routing.md` would result in the following parameters being available to the page: ```js // @noErrors { org: 'sveltejs', repo: 'kit', branch: 'main', file: 'documentation/docs/04-advanced-routing.md' } ``` > [!NOTE] `src/routes/a/[...rest]/z/+page.svelte` will match `/a/z` (i.e. there's no parameter at all) as well as `/a/b/z` and `/a/b/c/z` and so on. Make sure you check that the value of the rest parameter is valid, for example using a [matcher](#Matching). ### 404 pages Rest parameters also allow you to render custom 404s. Given these routes... ```tree 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`, because 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: ```tree src/routes/ ├ marx-brothers/ +++| ├ [...path]/+++ │ ├ chico/ │ ├ harpo/ │ ├ groucho/ │ └ +error.svelte └ +error.svelte ``` ```js /// file: src/routes/marx-brothers/[...path]/+page.js import { error } from '@sveltejs/kit'; /** @type {import('./$types').PageLoad} */ export function load(event) { error(404, 'Not Found'); } ``` > [!NOTE] If you don't handle 404 cases, they will appear in [`handleError`](hooks#Shared-hooks-handleError) ## 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/fruits/[page]` would match `/fruits/apple`, but it would also match `/fruits/rocketship`. We don't want that. You can ensure that route parameters are well-formed by adding a _matcher_ — which takes the parameter string (`"apple"` or `"rocketship"`) and returns `true` if it is valid — to your [`params`](configuration#files) directory... ```js /// file: src/params/fruit.js /** * @param {string} param * @return {param is ('apple' | 'orange')} * @satisfies {import('@sveltejs/kit').ParamMatcher} */ export function match(param) { return param === 'apple' || param === 'orange'; } ``` ...and augmenting your routes: ``` src/routes/fruits/[page+++=fruit+++] ``` 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. > [!NOTE] Matchers run both on the server and in the browser. ## Sorting It's possible for multiple routes to match a given path. For example each of these routes would match `/foo-abc`: ```bash 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](#Matching) (`[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: ```bash 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 special 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]` - `|` — `[x+7c]` - `#` — `[x+23]` - `%` — `[x+25]` - `[` — `[x+5b]` - `]` — `[x+5d]` - `(` — `[x+28]` - `)` — `[x+29]` For example, to create a `/smileys/:-)` route, you would create a `src/routes/smileys/[x+3a]-[x+29]/+page.svelte` file. You can determine the hexadecimal code for a character with JavaScript: ```js ':'.charCodeAt(0).toString(16); // '3a', hence '[x+3a]' ``` 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](https://unicodebook.readthedocs.io/unicode_encodings.html). > [!NOTE] Since TypeScript [struggles](https://github.com/microsoft/TypeScript/issues/13399) with directories with a leading `.` character, you may find it useful to encode these characters when creating e.g. [`.well-known`](https://en.wikipedia.org/wiki/Well-known_URI) routes: `src/routes/[x+2e]well-known/...` ## 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 (`/about` or `/testimonials`). We can group these routes with a directory whose name is wrapped in parentheses — unlike normal directories, `(app)` and `(marketing)` do not affect the URL pathname of the routes inside them: ```tree src/routes/ +++│ (app)/+++ │ ├ dashboard/ │ ├ 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 `(marketing)` page. ### Breaking out of layouts The root layout applies to every page of your app — if omitted, it defaults to `{@render children()}`. If you want some pages to have a different layout hierarchy than the rest, then you can put your entire app inside one or more groups _except_ the routes that should not inherit the common layouts. 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: ```tree 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: - `+page@[id].svelte` - inherits from `src/routes/(app)/item/[id]/+layout.svelte` - `+page@item.svelte` - inherits from `src/routes/(app)/item/+layout.svelte` - `+page@(app).svelte` - inherits from `src/routes/(app)/+layout.svelte` - `+page@.svelte` - inherits from `src/routes/+layout.svelte` ```tree src/routes/ ├ (app)/ │ ├ item/ │ │ ├ [id]/ │ │ │ ├ embed/ +++│ │ │ │ └ +page@(app).svelte+++ │ │ │ └ +layout.svelte │ │ └ +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 `+layout@.svelte` 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/+layout@.svelte │ │ │ └ +page.svelte // uses (app)/item/+layout@.svelte │ │ └ +layout@.svelte // inherits from root layout, skipping (app)/+layout.svelte │ └ +layout.svelte └ +layout.svelte ``` ### When to use layout groups Not all use cases are suited for layout grouping, nor should you feel compelled to use them. It might be that your use case would result in complex `(group)` nesting, or that you don't want to introduce a `(group)` for a single outlier. It's perfectly fine to use other means such as composition (reusable `load` functions or Svelte components) or if-statements to achieve what you want. The following example shows a layout that rewinds to the root layout and reuses components and functions that other layouts can also use: ```svelte {@render children()} ``` ```js /// file: src/routes/nested/route/+layout.js // @filename: ambient.d.ts declare module "$lib/reusable-load-function" { export function reusableLoad(event: import('@sveltejs/kit').LoadEvent): Promise>; } // @filename: index.js // ---cut--- import { reusableLoad } from '$lib/reusable-load-function'; /** @type {import('./$types').PageLoad} */ export function load(event) { // Add additional logic here, if needed return reusableLoad(event); } ``` ## Further reading - [Tutorial: Advanced Routing](/tutorial/kit/optional-params) # 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. There are three hooks files, all optional: - `src/hooks.server.js` — your app's server hooks - `src/hooks.client.js` — your app's client hooks - `src/hooks.js` — your app's hooks that run on both the client and server Code in these modules will run when the application starts up, making them useful for initializing database clients and so on. > [!NOTE] You can configure the location of these files with [`config.kit.files.hooks`](configuration#files). ## 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](web-standards#Fetch-APIs-Request) — whether that happens while the app is running, or during [prerendering](page-options#prerender) — and determines the [response](web-standards#Fetch-APIs-Response). It receives an `event` object representing 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 programmatically, for example). ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { if (event.url.pathname.startsWith('/custom')) { return new Response('custom response'); } const response = await resolve(event); return response; } ``` > [!NOTE] Requests for static assets — which includes pages that were already prerendered — are _not_ handled by SvelteKit. If unimplemented, defaults to `({ event, resolve }) => resolve(event)`. ### locals To add custom data to the request, which is passed to handlers in `+server.js` and server `load` functions, populate the `event.locals` object, as shown below. ```js /// file: src/hooks.server.js // @filename: ambient.d.ts type User = { name: string; } declare namespace App { interface Locals { user: User; } } const getUserInformation: (cookie: string | void) => Promise; // @filename: index.js // ---cut--- /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { event.locals.user = await getUserInformation(event.cookies.get('sessionid')); const response = await resolve(event); response.headers.set('x-custom-header', 'potato'); return response; } ``` You can define multiple `handle` functions and execute them with [the `sequence` helper function](@sveltejs-kit-hooks). `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: - `transformPageChunk(opts: { html: string, done: boolean }): MaybePromise` — applies custom transforms to HTML. If `done` is true, it's the final chunk. Chunks are not guaranteed to be well-formed HTML (they could include an element's opening tag but not its closing tag, for example) but they will always be split at sensible boundaries such as `%sveltekit.head%` or layout/page components. - `filterSerializedResponseHeaders(name: string, value: string): boolean` — determines which headers should be included in serialized responses when a `load` function loads a resource with `fetch`. By default, none will be included. - `preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean` — determines what files should be added to the `` tag to preload it. The method is called with each file that was found at build time while constructing the code chunks — so if you for example have `import './styles.css` in your `+page.svelte`, `preload` will be called with the resolved path to that CSS file when visiting that page. Note that in dev mode `preload` is _not_ called, since it depends on analysis that happens at build time. Preloading can improve performance by downloading assets sooner, but it can also hurt if too much is downloaded unnecessarily. By default, `js` and `css` files will be preloaded. `asset` files are not preloaded at all currently, but we may add this later after evaluating feedback. ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').Handle} */ export async function handle({ event, resolve }) { const response = await resolve(event, { transformPageChunk: ({ html }) => html.replace('old', 'new'), filterSerializedResponseHeaders: (name) => name.startsWith('x-'), preload: ({ type, path }) => type === 'js' || path.includes('/important/') }); return response; } ``` Note that `resolve(...)` will never throw an error, it will always return a `Promise` with the appropriate 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](errors). ### 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). ```js /// file: src/hooks.server.js /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ request, fetch }) { if (request.url.startsWith('https://api.yourapp.com/')) { // clone the original request, but change the URL request = new Request( request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'), request ); } 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 included in the request. 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`: ```js /// file: src/hooks.server.js // @errors: 2345 /** @type {import('@sveltejs/kit').HandleFetch} */ export async function handleFetch({ event, request, fetch }) { if (request.url.startsWith('https://api.my-domain.com/')) { request.headers.set('cookie', event.request.headers.get('cookie')); } 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](errors#Unexpected-errors) is thrown during loading or rendering, this function will be called with the `error`, `event`, `status` code and `message`. This allows for two things: - you can log the error - you can generate a custom representation of the error that is safe to show to users, omitting sensitive details like messages and stack traces. The returned value, which defaults to `{ message }`, becomes the value of `$page.error`. For errors thrown from your code (or library code called by your code) the status will be 500 and the message will be "Internal Error". While `error.message` may contain sensitive information that should not be exposed to users, `message` is safe (albeit meaningless to the average user). To add more information to the `$page.error` object in a type-safe way, you can customize the expected shape by declaring an `App.Error` interface (which must include `message: string`, to guarantee sensible fallback behavior). This allows you to — for example — append a tracking ID for users to quote in correspondence with your technical support staff: ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { message: string; errorId: string; } } } export {}; ``` ```js /// file: src/hooks.server.js // @errors: 2322 2353 // @filename: ambient.d.ts declare module '@sentry/sveltekit' { export const init: (opts: any) => void; export const captureException: (error: any, opts: any) => void; } // @filename: index.js // ---cut--- import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) /** @type {import('@sveltejs/kit').HandleServerError} */ export async function handleError({ error, event, status, message }) { const errorId = crypto.randomUUID(); // example integration with https://sentry.io/ Sentry.captureException(error, { extra: { event, errorId, status } }); return { message: 'Whoops!', errorId }; } ``` ```js /// file: src/hooks.client.js // @errors: 2322 2353 // @filename: ambient.d.ts declare module '@sentry/sveltekit' { export const init: (opts: any) => void; export const captureException: (error: any, opts: any) => void; } // @filename: index.js // ---cut--- import * as Sentry from '@sentry/sveltekit'; Sentry.init({/*...*/}) /** @type {import('@sveltejs/kit').HandleClientError} */ export async function handleError({ error, event, status, message }) { const errorId = crypto.randomUUID(); // example integration with https://sentry.io/ Sentry.captureException(error, { extra: { event, errorId, status } }); return { message: 'Whoops!', errorId }; } ``` > [!NOTE] In `src/hooks.client.js`, the type of `handleError` is `HandleClientError` instead of `HandleServerError`, and `event` is a `NavigationEvent` rather than a `RequestEvent`. This function is not called for _expected_ errors (those thrown with the [`error`](@sveltejs-kit#error) function imported from `@sveltejs/kit`). 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. > [!NOTE] Make sure that `handleError` _never_ throws an error ### init This function runs once, when the server is created or the app starts in the browser, and is a useful place to do asynchronous work such as initializing a database connection. > [!NOTE] If your environment supports top-level await, the `init` function is really no different from writing your initialisation logic at the top level of the module, but some environments — most notably, Safari — don't. ```js /// file: src/hooks.server.js import * as db from '$lib/server/database'; /** @type {import('@sveltejs/kit').ServerInit} */ export async function init() { await db.connect(); } ``` > [!NOTE] > In the browser, asynchronous work in `init` will delay hydration, so be mindful of what you put in there. ## Universal hooks The following can be added to `src/hooks.js`. Universal hooks run on both server and client (not to be confused with shared hooks, which are environment-specific). ### reroute This function runs before `handle` and allows you to change how URLs are translated into routes. The returned pathname (which defaults to `url.pathname`) is used to select the route and its parameters. For example, you might have a `src/routes/[[lang]]/about/+page.svelte` page, which should be accessible as `/en/about` or `/de/ueber-uns` or `/fr/a-propos`. You could implement this with `reroute`: ```js /// file: src/hooks.js // @errors: 2345 // @errors: 2304 /** @type {Record} */ const translated = { '/en/about': '/en/about', '/de/ueber-uns': '/de/about', '/fr/a-propos': '/fr/about', }; /** @type {import('@sveltejs/kit').Reroute} */ export function reroute({ url }) { if (url.pathname in translated) { return translated[url.pathname]; } } ``` The `lang` parameter will be correctly derived from the returned pathname. Using `reroute` will _not_ change the contents of the browser's address bar, or the value of `event.url`. ### transport This is a collection of _transporters_, which allow you to pass custom types — returned from `load` and form actions — across the server/client boundary. Each transporter contains an `encode` function, which encodes values on the server (or returns `false` for anything that isn't an instance of the type) and a corresponding `decode` function: ```js /// file: src/hooks.js import { Vector } from '$lib/math'; /** @type {import('@sveltejs/kit').Transport} */ export const transport = { Vector: { encode: (value) => value instanceof Vector && [value.x, value.y], decode: ([x, y]) => new Vector(x, y) } }; ``` ## Further reading - [Tutorial: Hooks](/tutorial/kit/handle) # 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](errors#Type-safety)). ## Expected errors An _expected_ error is one created with the [`error`](@sveltejs-kit#error) helper imported from `@sveltejs/kit`: ```js /// file: src/routes/blog/[slug]/+page.server.js // @filename: ambient.d.ts declare module '$lib/server/database' { export function getPost(slug: string): Promise<{ title: string, content: string } | undefined> } // @filename: index.js // ---cut--- import { error } from '@sveltejs/kit'; import * as db from '$lib/server/database'; /** @type {import('./$types').PageServerLoad} */ export async function load({ params }) { const post = await db.getPost(params.slug); if (!post) { error(404, { message: 'Not found' }); } return { post }; } ``` This throws an exception that SvelteKit catches, causing it to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `page.error` is the object provided as the second argument to `error(...)`. ```svelte

{page.error.message}

``` > [!LEGACY] > `$app/state` was added in SvelteKit 2.12. If you're using an earlier version or are using Svelte 4, use `$app/stores` instead. You can add extra properties to the error object if needed... ```js import { error } from '@sveltejs/kit'; declare global { namespace App { interface Error { message: string; code: string; } } } // ---cut--- error(404, { message: 'Not found', +++code: 'NOT_FOUND'+++ }); ``` ...otherwise, for convenience, you can pass a string as the second argument: ```js import { error } from '@sveltejs/kit'; // ---cut--- ---error(404, { message: 'Not found' });--- +++error(404, 'Not found');+++ ``` > [!NOTE] [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `error` yourself ## 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: ```json { "message": "Internal Error" } ``` Unexpected errors will go through the [`handleError`](hooks#Shared-hooks-handleError) hook, where you can add your own error handling — for example, sending errors to a reporting service, or returning a custom error object which becomes `$page.error`. ## Responses If an error occurs inside `handle` or inside a [`+server.js`](routing#server) 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: ```html %sveltekit.error.message%

My custom error page

Status: %sveltekit.status%

Message: %sveltekit.error.message%

``` SvelteKit will replace `%sveltekit.status%` and `%sveltekit.error.message%` with their corresponding values. If the error instead occurs inside a `load` function while rendering a page, SvelteKit will render the [`+error.svelte`](routing#error) 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 error 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'): ```ts /// file: src/app.d.ts declare global { namespace App { interface Error { +++ code: string; id: string;+++ } } } export {}; ``` This interface always includes a `message: string` property. ## Further reading - [Tutorial: Errors and redirects](/tutorial/kit/error-basics) - [Tutorial: Hooks](/tutorial/kit/handle) # Link options In SvelteKit, `
` elements (rather than framework-specific `` components) are used to navigate between 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 `` itself, or to a parent element. These options also apply to `` elements with [`method="GET"`](form-actions#GET-vs-POST). ## 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, preloading begins on `touchstart` - `"tap"` means that preloading will start as soon as a `touchstart` or `mousedown` event is registered The default project template has a `data-sveltekit-preload-data="hover"` attribute applied to the `` element in `src/app.html`, meaning that every link is preloaded on hover by default: ```html
%sveltekit.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 delay 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: ```html
Get current stonk values ``` > [!NOTE] You can also programmatically invoke `preloadData` from `$app/navigation`. Data will never be preloaded if the user has chosen reduced data usage, meaning [`navigator.connection.saveData`](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/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': - `"eager"` means that links will be preloaded straight away - `"viewport"` means that links will be preloaded once they enter the viewport - `"hover"` - as above, except that only code is preloaded - `"tap"` - as above, except that only code is preloaded Note that `viewport` and `eager` only apply to links that are present in the DOM immediately following navigation — if a link is added later (in an `{#if ...}` block, for example) it will not be preloaded until triggered by `hover` or `tap`. This is to avoid performance pitfalls resulting from aggressively observing the DOM for changes. > [!NOTE] 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... ```html Path ``` ...will cause a full-page navigation when the link is clicked. Links with a `rel="external"` attribute will receive the same treatment. In addition, they will be ignored during [prerendering](page-options#prerender). ## 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... ```html Path ``` ...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](accessibility#Focus-management) 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-sveltekit-keepfocus` attribute to it... ```html
``` ...will cause the currently focused element to retain focus after navigation. In general, avoid using this attribute on links, since the focused element would be the `` 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... ```html Path ``` ...will prevent scrolling after the link is clicked. ## Disabling options To disable any of these options inside an element where they have been enabled, use the `"false"` value: ```html
a b c
d e f
``` To apply an attribute to an element conditionally, do this: ```svelte
``` # Service workers Service workers act as proxy servers that handle network requests inside your app. This makes it possible to make your app work offline, but even if you don't need offline support (or can't realistically implement it because of the type of app you're building), it's often worth using service workers to speed up navigation by precaching your built JS and CSS. In SvelteKit, if you have a `src/service-worker.js` file (or `src/service-worker/index.js`) it will be bundled and automatically registered. You can change the [location of your service worker](configuration#files) if you need to. You can [disable automatic registration](configuration#serviceWorker) if you need to register the service worker with your own logic or use another solution. The default registration looks something like this: ```js if ('serviceWorker' in navigator) { addEventListener('load', function () { navigator.serviceWorker.register('./path/to/service-worker.js'); }); } ``` ## Inside the service worker Inside the service worker you have access to the [`$service-worker` module]($service-worker), which provides you with the paths to all static assets, build files and prerendered pages. You're also provided with an app version string, which you can use for creating a unique cache name, and the deployment's `base` path. If your Vite config specifies `define` (used for global variable replacements), this will be applied to service workers as well as your server/client builds. 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. ```js // @errors: 2339 /// import { build, files, version } from '$service-worker'; // Create a unique cache name for this deployment const CACHE = `cache-${version}`; const ASSETS = [ ...build, // the app itself ...files // everything in `static` ]; self.addEventListener('install', (event) => { // Create a new cache and add all files to it async function addFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); } event.waitUntil(addFilesToCache()); }); self.addEventListener('activate', (event) => { // Remove previous cached data from disk async function deleteOldCaches() { for (const key of await caches.keys()) { if (key !== CACHE) await caches.delete(key); } } event.waitUntil(deleteOldCaches()); }); self.addEventListener('fetch', (event) => { // ignore POST requests etc if (event.request.method !== 'GET') return; async function respond() { const url = new URL(event.request.url); const cache = await caches.open(CACHE); // `build`/`files` can always be served from the cache if (ASSETS.includes(url.pathname)) { const response = await cache.match(url.pathname); if (response) { return response; } } // for everything else, try the network first, but // fall back to the cache if we're offline try { const response = await fetch(event.request); // if we're offline, fetch can return a value that is not a Response // instead of throwing - and we can't pass this non-Response to respondWith if (!(response instanceof Response)) { throw new Error('invalid response from fetch'); } if (response.status === 200) { cache.put(event.request, response.clone()); } return response; } catch (err) { const response = await cache.match(event.request); if (response) { return response; } // if there's no cache, then just error out // as there is nothing we can do to respond to this request throw err; } } event.respondWith(respond()); }); ``` > [!NOTE] 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](https://web.dev/es-modules-in-sw) will be able to use them at dev time. If you are manually registering your service worker, you will need to pass the `{ type: 'module' }` option in development: ```js import { dev } from '$app/environment'; navigator.serviceWorker.register('/service-worker.js', { type: dev ? 'module' : 'classic' }); ``` > [!NOTE] `build` and `prerendered` are empty arrays during development ## 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: ```original-js /// /// /// /// const sw = /** @type {ServiceWorkerGlobalScope} */ (/** @type {unknown} */ (self)); ``` ```generated-ts /// /// /// /// const sw = self as unknown as ServiceWorkerGlobalScope; ``` 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 this is 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. If you import `$env/static/public` you either have to `// @ts-ignore` the import or add `/// ` to the reference types. ## Other solutions SvelteKit's service worker implementation is deliberately low-level. If you need a more full-fledged but also more opinionated solution, we recommend looking at solutions like [Vite PWA plugin](https://vite-pwa-org.netlify.app/frameworks/sveltekit.html), which uses [Workbox](https://web.dev/learn/pwa/workbox). For more general information on service workers, we recommend [the MDN web docs](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers). # 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 variables containing API keys, for example). SvelteKit provides a way to prevent this entirely: server-only modules. ## Private environment variables The [`$env/static/private`]($env-static-private) and [`$env/dynamic/private`]($env-dynamic-private) modules can only be imported into modules that only run on the server, such as [`hooks.server.js`](hooks#Server-hooks) or [`+page.server.js`](routing#page-page.server.js). ## Server-only utilities The [`$app/server`]($app-server) module, which contains a [`read`]($app-server#read) function for reading assets from the filesystem, can likewise only be imported by code that runs on the server. ## Your modules You can make your own modules server-only in two ways: - adding `.server` to the filename, e.g. `secrets.server.js` - placing them in `$lib/server`, e.g. `$lib/server/secrets.js` ## How it works Any time you have public-facing code that imports server-only code (whether directly or indirectly)... ```js // @errors: 7005 /// file: $lib/server/secrets.js export const atlantisCoordinates = [/* redacted */]; ``` ```js // @errors: 2307 7006 7005 /// file: src/routes/utils.js export { atlantisCoordinates } from '$lib/server/secrets.js'; export const add = (a, b) => a + b; ``` ```html /// file: src/routes/+page.svelte ``` ...SvelteKit will error: ``` Cannot import $lib/server/secrets.js into public-facing code: - src/routes/+page.svelte - src/routes/utils.js - $lib/server/secrets.js ``` 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 downloads, 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. > [!NOTE] 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.TEST === 'true'`. ## Further reading - [Tutorial: Environment variables](/tutorial/kit/env-static-private) # Snapshots Ephemeral DOM state — like scroll positions on sidebars, the content of `` elements and so on — is discarded when you navigate from one page to another. For example, if the user fills out a form but navigates away and then back before submitting, or if the user refreshes the page, 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`: ```svelte