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).
Layout with nested routes
Section titled “Layout with nested routes”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.
// 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> );}<!-- 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().
Active link highlighting
Section titled “Active link highlighting”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 Router — active link with useLocationimport { 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> );}<!-- +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.
Navigating with plain <a href>
Section titled “Navigating with plain <a href>”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.
$app/state and the page object
Section titled “$app/state and the page object”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:
| Property | React equivalent |
|---|---|
page.url.pathname | useLocation().pathname / usePathname() |
page.params | useParams() |
page.url.searchParams | useSearchParams() |
page.data | Data returned by a load function |
page.status | HTTP status code of the current page |
Full +layout.svelte with active-link navigation
Section titled “Full +layout.svelte with active-link navigation”Here is a complete layout you can drop into any SvelteKit project:
<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>Nested layouts
Section titled “Nested layouts”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"]
/dashboard/settings receives both the root layout and the dashboard layout, nested — no extra configuration required.
Key points
Section titled “Key points”+layout.sveltewraps 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.sveltewraps 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. pagefrom$app/stateprovidesurl,params,data,route.id, and more — all reactive.page.url.pathnameis 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.