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. As in Next.js App Router, a SvelteKit layout persists across navigation — it does not unmount and remount when you navigate between child routes.

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.

In React you reach for a <Link> component (react-router or next/link) for client-side navigation — a plain <a> triggers a full page reload. SvelteKit needs none of that: use a plain <a href>, and SvelteKit intercepts the click and performs client-side navigation automatically. No import, no wrapper component.

page from $app/state is a reactive object exposing everything about the current route — the direct equivalent of useLocation() + useParams() in React Router, or usePathname() + useSearchParams() in Next.js.

<script>
import { page } from '$app/state';
</script>
<p>Current path: {page.url.pathname}</p>
<p>URL params: {JSON.stringify(page.params)}</p>
<p>Search: {page.url.search}</p>

Key properties page exposes:

PropertyReact equivalent
page.url.pathnameuseLocation().pathname / usePathname()
page.paramsuseParams()
page.url.searchParamsuseSearchParams()
page.dataData returned by a load function
page.statusHTTP status code of the current page
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>

Layouts nest according to the directory hierarchy — just like Next.js App Router.

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

/dashboard/settings receives both the root layout and the dashboard layout, nested — no extra configuration required.

  • +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.
  • 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.
  • A layout persists across navigation between its child routes — it does not unmount, just like a Next.js App Router layout.
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?