Sveltekit Routing
SvelteKit file-based routing system including layouts, groups, and advanced route patterns
You are an expert in SvelteKit's file-based routing system, helping developers structure applications with pages, layouts, route parameters, groups, and navigation.
## Key Points
- **Co-locate related files.** Keep `+page.svelte`, `+page.server.js`, and `+error.svelte` in the same route directory for clear ownership.
- **Use layout groups** to share layouts across unrelated URL paths without nesting them under a common URL prefix.
- **Validate parameters** with matchers instead of runtime checks in load functions.
- **Prefer `<a>` tags over `goto()`** for standard navigation — it enables SvelteKit's client-side router and keeps links accessible.
- **Use shallow routing** (`pushState`/`replaceState` from `$app/navigation`) for modals and tabs that should update the URL without a full navigation.
- **Missing `+layout.svelte` slot.** Forgetting `{@render children()}` (Svelte 5) in a layout causes child pages to not render.
- **Ambiguous routes.** Two sibling dynamic parameters (`[slug]` and `[id]`) at the same level conflict. Use matchers to disambiguate.
- **Trailing slashes.** Configure `trailingSlash` in `svelte.config.js` to avoid duplicate routes and SEO issues.
- **Forgetting `+page.svelte`.** A directory without `+page.svelte` does not produce a route, even if it has a layout or load function.
## Quick Example
```
src/routes/
docs/[...path]/
+page.svelte → /docs/a, /docs/a/b/c
```
```
src/routes/
[[lang]]/about/
+page.svelte → /about, /en/about, /fr/about
```skilldb get svelte-skills/Sveltekit RoutingFull skill: 251 linesRouting and Layouts — SvelteKit
You are an expert in SvelteKit's file-based routing system, helping developers structure applications with pages, layouts, route parameters, groups, and navigation.
Core Philosophy
SvelteKit's file-based routing turns the file system into the single source of truth for your application's URL structure. This is a stronger commitment than configuration-based routing: the directory tree is not an approximation of the routes — it is the routes. A new developer can open src/routes/ and immediately understand every URL the application serves, what layout wraps each page, and where error boundaries catch failures. This transparency is the primary design goal.
Co-location is the architectural principle that makes SvelteKit routing powerful. A route directory contains everything that route needs: the page component (+page.svelte), the data loader (+page.server.js or +page.js), the error boundary (+error.svelte), and optionally the form actions. When all of a route's concerns live together, navigating the codebase follows the same mental model as navigating the application. You never need to cross-reference a routes configuration file with a components directory and a separate data-fetching layer.
Layout groups (parenthesized directories) and layout resets (+page@.svelte) provide escape hatches for when the URL hierarchy and the layout hierarchy diverge. A marketing site and a dashboard app might share the same domain but need completely different layouts. Groups let you apply different layouts to different route subtrees without affecting the URL structure. This flexibility means the file system convention never becomes a constraint.
Anti-Patterns
-
Missing
{@render children()}in Layouts — creating a+layout.sveltethat wraps content but forgets to render the child page, causing all child routes to display a blank page with only the layout chrome. -
Ambiguous Dynamic Route Siblings — placing
[slug]/and[id]/at the same directory level without parameter matchers, making it impossible for the router to determine which should match. Use matchers like[id=integer]to disambiguate. -
Relying on Layout State Resetting Between Siblings — expecting component state in a layout to reset when navigating from
/blog/ato/blog/b. Layouts persist across sibling navigations, so any state must be explicitly reset using$effector key blocks. -
Using
goto()for Standard Navigation — callinggoto('/about')in a click handler when a plain<a href="/about">link would provide the same navigation with better accessibility, prefetching, and progressive enhancement. -
Directory Without
+page.svelte— creating a route directory with a layout and load function but no+page.svelte, expecting it to produce a route. Without the page component, the directory is inert.
Overview
SvelteKit uses a file-system-based router where the directory structure under src/routes defines the application's URL routes. Each route can have a page component, layout, server endpoints, error boundaries, and load functions, all co-located in the route directory.
Core Concepts
Basic Pages
A +page.svelte file inside a route directory renders that route's page.
src/routes/
+page.svelte → /
about/
+page.svelte → /about
blog/
+page.svelte → /blog
Route Parameters
Dynamic segments are denoted with square brackets.
src/routes/
blog/
[slug]/
+page.svelte → /blog/hello-world, /blog/my-post
[category]/[slug]/
+page.svelte → /blog/tech/my-post
Accessed via the params object in load functions or $page.params in components:
<script>
import { page } from '$app/stores';
// or in a load function: export function load({ params }) { ... }
</script>
<h1>Post: {$page.params.slug}</h1>
Rest Parameters
Catch-all segments use [...rest]:
src/routes/
docs/[...path]/
+page.svelte → /docs/a, /docs/a/b/c
Optional Parameters
Use double brackets for optional segments:
src/routes/
[[lang]]/about/
+page.svelte → /about, /en/about, /fr/about
Parameter Matchers
Constrain parameters with named matchers:
// src/params/integer.js
export function match(param) {
return /^\d+$/.test(param);
}
src/routes/
items/[id=integer]/
+page.svelte → /items/42 (matches), /items/abc (does not)
Layouts
A +layout.svelte applies to all child routes. It must render a {@render children()} slot (Svelte 5).
<!-- src/routes/+layout.svelte -->
<script>
let { children } = $props();
</script>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
{@render children()}
</main>
<footer>My App</footer>
Layouts nest automatically — a layout at src/routes/dashboard/+layout.svelte wraps all pages under /dashboard/* and is itself wrapped by the root layout.
Layout Groups
Group routes that share a layout without affecting the URL by using parenthesized directory names:
src/routes/
(marketing)/
+layout.svelte ← shared marketing layout
pricing/
+page.svelte → /pricing
features/
+page.svelte → /features
(app)/
+layout.svelte ← shared app layout (with sidebar)
dashboard/
+page.svelte → /dashboard
settings/
+page.svelte → /settings
Breaking Out of Layouts
Use +page@.svelte or +page@<segment>.svelte to reset to a specific parent layout:
src/routes/
(app)/
+layout.svelte
dashboard/
+page.svelte ← uses (app) layout
dashboard/fullscreen/
+page@.svelte ← resets to root layout
Implementation Patterns
Programmatic Navigation
<script>
import { goto } from '$app/navigation';
async function handleSubmit() {
await saveData();
goto('/dashboard');
}
</script>
Active Link Highlighting
<script>
import { page } from '$app/stores';
</script>
<nav>
{#each links as link}
<a
href={link.href}
class:active={$page.url.pathname === link.href}
>
{link.label}
</a>
{/each}
</nav>
Error Pages
Co-locate +error.svelte with routes to catch errors at that level:
<!-- src/routes/blog/[slug]/+error.svelte -->
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error?.message}</h1>
API Routes (Server Endpoints)
+server.js files define API endpoints:
// src/routes/api/users/+server.js
import { json } from '@sveltejs/kit';
export async function GET({ url }) {
const limit = Number(url.searchParams.get('limit') ?? 10);
const users = await db.getUsers(limit);
return json(users);
}
export async function POST({ request }) {
const body = await request.json();
const user = await db.createUser(body);
return json(user, { status: 201 });
}
Best Practices
- Co-locate related files. Keep
+page.svelte,+page.server.js, and+error.sveltein the same route directory for clear ownership. - Use layout groups to share layouts across unrelated URL paths without nesting them under a common URL prefix.
- Validate parameters with matchers instead of runtime checks in load functions.
- Prefer
<a>tags overgoto()for standard navigation — it enables SvelteKit's client-side router and keeps links accessible. - Use shallow routing (
pushState/replaceStatefrom$app/navigation) for modals and tabs that should update the URL without a full navigation.
Common Pitfalls
- Missing
+layout.svelteslot. Forgetting{@render children()}(Svelte 5) in a layout causes child pages to not render. - Ambiguous routes. Two sibling dynamic parameters (
[slug]and[id]) at the same level conflict. Use matchers to disambiguate. - Layout reuse across navigations. Layouts persist across sibling page navigations. Component state in a layout is not reset when moving between
/blog/aand/blog/b— use$effector key blocks if reset is needed. - Trailing slashes. Configure
trailingSlashinsvelte.config.jsto avoid duplicate routes and SEO issues. - Forgetting
+page.svelte. A directory without+page.sveltedoes not produce a route, even if it has a layout or load function.
Install this skill directly: skilldb add svelte-skills
Related Skills
Component Patterns
Svelte component composition patterns including props, snippets, context, and advanced reuse techniques
Form Actions
SvelteKit form actions for progressive enhancement with server-side form handling
Load Functions
SvelteKit server and universal load functions for fetching and passing data to pages and layouts
Reactivity
Svelte 5 runes system for fine-grained reactivity with $state, $derived, and $effect
Stores
Svelte stores and state management patterns including writable, readable, derived, and custom stores
Sveltekit Auth
Authentication patterns in SvelteKit using hooks, cookies, sessions, and OAuth flows