Nested Layouts
Sharing UI and data across groups of pages with +layout.svelte, and how layouts nest.
2 min de lecture
Most pages on a site share something — a nav bar, a footer, a sidebar. Copy-pasting that markup into every +page.svelte would be a maintenance disaster. SvelteKit solves this with +layout.svelte.
A basic layout
<!-- src/routes/+layout.svelte -->
<script>
let { children } = $props();
</script>
<header>
<nav>
<a href="/">Home</a>
<a href="/blog">Blog</a>
</nav>
</header>
<main>
{@render children()}
</main>
<footer>© 2026 My Site</footer>{@render children()} is where the matched page renders. A +layout.svelte at the root of src/routes wraps every page in the app — you get the header, nav, and footer on every route without writing them more than once.
Layouts nest with folders
Just like pages, layouts follow the folder structure, and they stack:
src/routes/
├── +layout.svelte # wraps everything
├── +page.svelte → /
└── blog/
├── +layout.svelte # wraps everything under /blog
├── +page.svelte → /blog
└── [slug]/
└── +page.svelte → /blog/[slug]
Visiting /blog/my-post renders the root layout, which renders the blog layout, which renders the page — each one wrapping the next via its own {@render children()}. This is how you add a blog-specific sidebar without it leaking into / or /about.
<!-- src/routes/blog/+layout.svelte -->
<script>
let { children } = $props();
</script>
<div class="blog-layout">
<aside>
<h3>Recent posts</h3>
<!-- ...list of links... -->
</aside>
<div class="content">
{@render children()}
</div>
</div>Escaping a layout
Sometimes a route needs to not inherit a layout — an auth page that shouldn't show the main nav, for example. Wrapping a route segment in parentheses creates a "layout group" that doesn't affect the URL but lets you opt out of the parent layout by giving that group its own root-relative layout:
src/routes/
├── +layout.svelte
├── (app)/
│ ├── +layout.svelte # inherits the root layout, adds app chrome
│ └── dashboard/
│ └── +page.svelte
└── (auth)/
└── login/
└── +page.svelte # inherits only the root layout
The parentheses ((app), (auth)) are invisible to the URL — /dashboard and /login are unaffected — but they let you group routes that share layout needs without those groups becoming part of the path.
Loading data for a layout
A +layout.js or +layout.server.js works exactly like a page's load function (the subject of the next lesson), except the data it returns is available to every page nested inside it — useful for things like "the currently logged-in user," fetched once and shared down the whole tree instead of refetched per page.
// src/routes/(app)/+layout.server.js
export async function load({ locals }) {
return { user: locals.user };
}Every page under (app)/ can then read user from its own data prop, without fetching it again — layouts aren't just for shared markup, they're for shared data too.