Learn how SvelteKit’s filesystem-based router works to define your application routes
At the heart of SvelteKit is a filesystem-based router. The routes of your app — the URL paths that users can access — are defined by the directories in your codebase.
A +page.svelte component defines a page of your app. By default, pages are rendered both on the server (SSR) for the initial request and in the browser (CSR) for subsequent navigation.
Copy
<!--- file: src/routes/+page.svelte ---><h1>Hello and welcome to my site!</h1><a href="/about">About my site</a>
SvelteKit uses <a> elements to navigate between routes, rather than a framework-specific <Link> component.
Often, a page will need to load some data before it can be rendered. Add a +page.js module that exports a load function:
Copy
/// file: src/routes/blog/[slug]/+page.jsimport { 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.
If your load function can only run on the server — for example, if it needs to fetch data from a database or access private environment variables — rename +page.js to +page.server.js:
Copy
/// file: src/routes/blog/[slug]/+page.server.jsimport { 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) { 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.
You can define routes with a +server.js file (API routes), which gives you full control over the response:
Copy
/// file: src/routes/api/random-number/+server.jsimport { 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));}
If an error occurs during load, SvelteKit will render a default error page. Customize this error page on a per-route basis by adding an +error.svelte file:
SvelteKit will ‘walk up the tree’ looking for the closest error boundary. If no +error.svelte file exists, it will try parent directories before rendering the default error page.
SvelteKit creates a $types.d.ts file for type safety when working with route files:
Copy
<!--- file: src/routes/blog/[slug]/+page.svelte ---><script> /** @type {import('./$types').PageProps} */ let { data } = $props();</script>
If you’re using VS Code or any IDE that supports the language server protocol and TypeScript plugins, you can omit these types entirely — Svelte’s IDE tooling will insert the correct types for you.