One File to Rule Them All

Imagine you’re building a house. Every room needs walls, a floor, and a ceiling — but each room has different furniture and decorations inside. In web development, a layout is like those walls, floor, and ceiling: the structure that stays the same across multiple pages.

A layout file wraps page content with shared UI. Create it once, and every page in that route automatically inherits the wrapper. This means you write your header, footer, or navigation once, and SvelteKit automatically places your page content inside it. No copy-pasting, no duplication. Let’s build BookIt’s first layout.


What You’ll Learn

  • Create a +layout.svelte file
  • Understand layout file placement
  • See how layouts wrap page content

The Root Layout

When we first created our app with Svelte CLI, the setup process generated the basic project structure for us, including a +layout.svelte file alongside the root +page.svelte in the src/routes directory.

What is this file for? The root layout file (src/routes/+layout.svelte) serves as the master wrapper for every page in our app. Any content we put in this layout will appear on all pages, with each page’s unique content inserted where we specify.

Let’s look at what’s inside this file and understand each piece:

<!-- filename: src/routes/+layout.svelte -->
<script>
	let { children } = $props()
</script>

<div class="app">
	<!-- add this as test  -->
	<p>CONTENT 1</p>

	{@render children()}

	<!-- add this as test  -->
	<p>CONTENT 2</p>
</div>

What Just Happened?

Here’s where the magic becomes visible. Visit any page in BookIt: the homepage (/), services (/services), about (/about), or contact (/contact). You’ll see the same pattern on every single one:

CONTENT 1

[Your page's unique content]

CONTENT 2

Every page now has “CONTENT 1” above it and “CONTENT 2” below it. The amazing part? We didn’t touch any page files. We only created one layout file, and SvelteKit automatically wraps every page with it.

This is the power of layouts: write once, apply everywhere. When you need to update your header or footer later, you’ll change one file instead of hunting through dozens of pages.


How Layout Wrapping Works

Think of the layout as a frame around a picture. The layout is the frame, the page is the picture:

┌─────────────────────────────┐
│      LAYOUT (top)           │  ← Your header, nav, etc.
│  ┌───────────────────────┐  │
│  │                       │  │
│  │    PAGE CONTENT       │  │  ← Changes based on route
│  │                       │  │
│  └───────────────────────┘  │
│      LAYOUT (bottom)        │  ← Your footer, scripts, etc.
└─────────────────────────────┘

The {@render children()} directive (we will cover in more detail in next lesson) marks where the page content form children prop goes. Everything else that is added on this .

Here’s what happens when someone visits /about:

  1. SvelteKit sees the request for /about
  2. It loads src/routes/about/+page.svelte
  3. It wraps that page with src/routes/+layout.svelte
  4. Where the layout says {@render children()}, it inserts the about page content
  5. The browser receives one complete HTML page with layout + page combined

When they navigate to /contact, the same process happens — but now the contact page content gets inserted into the layout instead. The layout itself never changes; only the page content inside it changes.


Clean Up Page Navigation

Now that we have a layout, we can remove duplicated code from our pages. This is one of the biggest benefits of layouts: eliminating repetition.

Before layouts: You probably had navigation links copied into every page. If you wanted to change the navigation, you’d have to update every single file.

After layouts: Put navigation in the layout once, and remove it from all pages. Now changing navigation means editing one file.

so place you nav in the layout file like this:

<!-- filename: src/routes/+layout.svelte -->
<script>
	import favicon from '$lib/assets/favicon.svg';

	let { children } = $props();
</script>

<svelte:head>
	<link rel="icon" href={favicon} />
</svelte:head>

<nav>
	<a href="/">BookIt</a>
	<a href="/services">Services</a>
	<a href="/about">About</a>
	<a href="/contact">Contact</a>
</nav>

{@render children()}

</section>

No more navigation code on every page. The layout handles it (we’ll add real navigation in future lessons). Each page should only contain its unique content.


Your Project Structure

Here’s how your project looks now, with annotations showing what gets wrapped:

src/routes/
├── +layout.svelte    ← The master wrapper for everything below
├── +page.svelte      ← Homepage (wrapped by layout)
├── services/
│   ├── +page.svelte  ← Services list (wrapped by layout)
│   └── [slug]/
│       └── +page.svelte  ← Service detail (wrapped by layout)
├── about/
│   └── +page.svelte  ← About page (wrapped by layout)
└── contact/
    └── +page.svelte  ← Contact page (wrapped by layout)

The key rule: A +layout.svelte file wraps its sibling +page.svelte file and all files in folders below it.

Since our layout is at the root (src/routes/+layout.svelte), it wraps the root page (src/routes/+page.svelte) and every page in every subfolder (services/, about/, contact/, etc.).

Later, we will create a layout at src/routes/services/+layout.svelte that only wraps pages in the services section to achieve different design. It is called nested design and we explore it in future lessons.


Common Mistakes

Forgetting to Destructure children

<!-- Missing destructuring -->
<script>
	let props = $props()
</script>

{@render children()} <!-- ERROR: 'children' is undefined -->

Creating the File in the Wrong Location

src/
├── routes/
│   └── +page.svelte
└── +layout.svelte    ← Wrong! Must be inside routes/

The layout file must be inside src/routes/, not alongside it.


Summary

You’ve created BookIt’s first layout and learned how SvelteKit automatically wraps all your pages with shared UI.

Key takeaways:

  • +layout.svelte in src/routes/ wraps all pages in your app
  • The layout receives page content via the children prop
  • You must destructure children from $props() to receive it
  • You must use {@render children()} to actually display the page content
  • Everything above {@render children()} appears before page content
  • Everything below it appears after page content
  • Layouts eliminate code duplication across pages
  • One layout file can wrap dozens or hundreds of pages automatically

What you can do now:

  • Create consistent headers, footers, and navigation
  • Update shared UI in one place instead of editing every page
  • Build more complex page structures without repetition

Next Steps

The placeholder text isn’t useful. Continue with Understanding {@render children()} to see exactly how content flows from pages into layouts.