Skip to content

Layouts and Navigation

In React Router you compose layouts by nesting <Outlet /> inside a parent route component — any child route renders where you place the outlet. SvelteKit takes a file-system approach instead: a +layout.svelte file automatically wraps every route in the same directory. You get the same nesting behaviour, but without any explicit route configuration.

Run this in a SvelteKit project (npx sv create).

In React Router you mark where children render by placing <Outlet />. In SvelteKit Svelte 5, layouts use the snippet syntax {@render children()} — there is no <slot /> in Svelte 5 layouts.

React
// src/layouts/RootLayout.jsx (React Router v6)
import { Outlet, Link } from 'react-router-dom';
export default function RootLayout() {
return (
<div>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/blog">Blog</Link>
</nav>
<main>
<Outlet />
</main>
</div>
);
}
Svelte
<!-- src/routes/+layout.svelte -->
<script>
let { children } = $props();
</script>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/blog">Blog</a>
</nav>
<main>
{@render children()}
</main>

{@render children()} is where the matched child route renders. The children snippet is provided automatically by SvelteKit — you just destructure it from $props().

React Router gives you useLocation() to read the current URL and derive active styles. SvelteKit exposes the same information through the page object imported from $app/state.

React
// React Router — active link with useLocation
import { Link, useLocation } from 'react-router-dom';
const links = [
{ to: '/', label: 'Home' },
{ to: '/about', label: 'About' },
{ to: '/blog', label: 'Blog' },
];
export default function Nav() {
const { pathname } = useLocation();
return (
<nav>
{links.map(({ to, label }) => (
<Link
key={to}
to={to}
className={pathname === to ? 'active' : ''}
>
{label}
</Link>
))}
</nav>
);
}
Svelte
<!-- +layout.svelte (Svelte 5) -->
<script>
import { page } from '$app/state';
let { children } = $props();
const links = [
{ href: '/', label: 'Home' },
{ href: '/about', label: 'About' },
{ href: '/blog', label: 'Blog' },
];
</script>
<nav>
{#each links as { href, label }}
<a {href} class:active={page.url.pathname === href}>
{label}
</a>
{/each}
</nav>
<main>
{@render children()}
</main>
<style>
a { margin-right: 1rem; text-decoration: none; color: inherit; }
a.active { font-weight: bold; border-bottom: 2px solid currentColor; }
</style>

page from $app/state is a reactive object — it updates on every navigation automatically. No useEffect, no subscription setup.

Section titled “Full +layout.svelte with active-link navigation”

Here is a complete layout you can drop into any SvelteKit project:

src/routes/+layout.svelte
<script>
import { page } from '$app/state';
let { children } = $props();
</script>
<header>
<a href="/" class="logo">MySite</a>
<nav>
<a href="/" class:active={page.url.pathname === '/'}>Home</a>
<a href="/about" class:active={page.url.pathname === '/about'}>About</a>
<a href="/blog" class:active={page.url.pathname.startsWith('/blog')}>Blog</a>
</nav>
</header>
<main>
{@render children()}
</main>
<footer>© 2026 MySite</footer>
<style>
header {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.logo { font-weight: 700; text-decoration: none; }
nav { display: flex; gap: 1rem; margin-left: auto; }
nav a { text-decoration: none; color: inherit; }
nav a.active { font-weight: 600; color: #FF3E00; }
</style>
  • +layout.svelte wraps all routes in the same directory — and nested directories inherit it automatically.
  • Children render at {@render children()} (Svelte 5 snippet syntax). There is no <slot /> in Svelte 5 layouts.
  • Layouts nest: dashboard/+layout.svelte wraps all routes under /dashboard, inside the root layout.
flowchart TB
  routes(["src/routes/"])
  routes --> rootLayout["+layout.svelte (root layout — nav + footer)"]
  routes --> rootPage["+page.svelte → /"]
  routes --> dashboard["dashboard/"]
  dashboard --> dashLayout["+layout.svelte (dashboard layout — sidebar)"]
  dashboard --> dashPage["+page.svelte → /dashboard"]
  dashboard --> settings["settings/"]
  settings --> settingsPage["+page.svelte → /dashboard/settings"]
Nested layouts in SvelteKit
  • Use plain <a href="/about"> — no <Link> component needed. SvelteKit intercepts <a> clicks and performs client-side navigation automatically.
  • page from $app/state provides url, params, data, route.id, and more — all reactive.
  • page.url.pathname is the idiomatic way to implement active-link logic.
Where does a child route render in a SvelteKit Svelte 5 layout?
How do you get the current pathname in a SvelteKit component?
You want client-side navigation to /dashboard in SvelteKit. What do you write?