Skip to main content
By default, SvelteKit will render any component first on the server and send it to the client as HTML. It will then render the component again in the browser to make it interactive in a process called hydration. You can control each of these on a page-by-page basis by exporting options from +page.js, +page.server.js, +layout.js, or +layout.server.js.
Child layouts and pages override values set in parent layouts, allowing you to configure different behaviors for different parts of your app.

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.
/// file: +page.js/+page.server.js/+server.js
export const prerender = true;

Prerendering everything

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:
/// file: +page.js/+page.server.js/+server.js
export const prerender = false;

Auto mode

Routes with prerender = true will be excluded from manifests used for dynamic SSR. In some cases you might want to prerender a route but also include it in the manifest:
/// file: +page.js/+page.server.js/+server.js
export const prerender = 'auto';
If your entire app is suitable for prerendering, you can use adapter-static, which will output files suitable for use with any static webserver.

How prerendering works

The prerenderer will start at the root of your app and generate files for any prerenderable pages or +server.js routes it finds:
1

Entry points

The prerenderer starts from configured entry points and crawls <a> elements
2

Discovery

Each page is scanned for links to other prerenderable pages
3

Generation

HTML files are generated for all discovered prerenderable routes
You can specify which pages should be accessed by the prerenderer using config.kit.prerender.entries, or by exporting an entries function from a dynamic route.

When not to prerender

The basic rule is: for a page to be prerenderable, any two users hitting it directly must get the same content from the server.
Not all pages are suitable for prerendering. Any content that is prerendered will be seen by all users. You can fetch personalized data in onMount in a prerendered page, but this may result in a poorer user experience.
Pages that cannot be prerendered:
Pages with form actions cannot be prerendered, because a server must be able to handle the action POST requests.
Accessing url.searchParams during prerendering is forbidden. If you need to use it, ensure you are only doing so in the browser (for example in onMount).

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:
src/routes/foo/+server.js        → foo (file)
src/routes/foo/bar/+server.js    → foo/bar (needs foo to be a directory)
It’s recommended that you always include a file extension to avoid conflicts. For pages, SvelteKit writes foo/index.html instead of foo.

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:
/             # non-dynamic
/blog         # non-dynamic
/blog/[slug]  # dynamic, needs manual entry
You can specify entries for dynamic routes by exporting an entries function:
/// 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 retrieve a list of posts from a CMS or database.

ssr

Normally, SvelteKit renders your page on the server before sending that HTML to the client where it’s hydrated. If you set ssr to false, it renders an empty ‘shell’ page instead:
/// file: +page.js
export const ssr = false;
This is useful if your page is unable to be rendered on the server (because you use browser-only globals like document), but in most situations it’s not recommended. See the appendix on SSR for more information.
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.You should not do this if your goal is to build a statically generated site.

csr

Ordinarily, SvelteKit hydrates your server-rendered HTML into an interactive client-side-rendered page. Some pages don’t require JavaScript at all:
/// file: +page.js
export const csr = false;
Disabling CSR means:

No JavaScript

The webpage works with HTML and CSS only

No scripts

<script> tags inside components are removed

No progressive enhancement

<form> elements cannot be progressively enhanced

Browser navigation

Links are handled by the browser with full-page navigation
You can enable csr during development (for HMR) like so:
import { dev } from '$app/environment';

export const csr = dev;

trailingSlash

By default, SvelteKit will remove trailing slashes from URLs — if you visit /about/, it will respond with a redirect to /about. You can change this behavior with the trailingSlash option:
/// file: src/routes/+layout.js
export const trailingSlash = 'never';
/about/ redirects to /about
Ignoring trailing slashes is not recommended — the semantics of relative paths differ between the two cases, and /x and /x/ are treated as separate URLs which is harmful to SEO.
This option also affects prerendering:
/about → about/index.html

config

With the concept of adapters, SvelteKit is able to run on a variety of platforms. Each of these might have specific configuration:
/// file: src/routes/+page.js
/** @type {import('some-adapter').Config} */
export const config = {
	runtime: 'edge'
};
config objects are merged at the top level (but not deeper levels):
1

Layout config

/// file: src/routes/+layout.js
export const config = {
	runtime: 'edge',
	regions: 'all',
	foo: {
		bar: true
	}
}
2

Page config

/// file: src/routes/+page.js
export const config = {
	regions: ['us1', 'us2'],
	foo: {
		baz: true
	}
}
3

Resulting config

{
	runtime: 'edge',
	regions: ['us1', 'us2'],
	foo: { baz: true }  // Note: bar is not preserved
}
Consult the documentation of your adapter for specific configuration options and type definitions.

Controlling page behavior

You can mix and match these options in different areas of your app:

Marketing pages

Prerender for maximum speed

Dynamic pages

Server-render for SEO and accessibility

Admin section

Client-only rendering for SPA behavior
This versatility makes SvelteKit suitable for a wide range of applications.

Next steps