Where Does Page Content Go?

The layout wraps pages, but how exactly does page content end up inside the layout? Understanding {@render children()} is key to mastering layouts.


What You’ll Learn

  • How children represents page content
  • The rendering flow from page to layout
  • Positioning {@render children()} for different layouts

The Content Flow

When you visit /about, here’s what happens:

  1. SvelteKit finds src/routes/about/+page.svelte
  2. SvelteKit also finds src/routes/+layout.svelte
  3. The page content of /about/+page.svelte becomes the children snippet
  4. The layout renders, calling {@render children()} where appropriate
  5. The page content appears at that exact position
/about request


┌─────────────────────────────────┐
│  +layout.svelte                 │
│  ┌───────────────────────────┐  │
│  │  about/+page.svelte       │  │
│  │  (rendered as children)   │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

Positioning Content

The position of {@render children()} determines where page content appears. You control this:

<!-- Content at the top -->
<script>
  let { children } = $props();
</script>

{@render children()}
<footer>Always at bottom</footer>
<!-- Content in the middle -->
<script>
  let { children } = $props();
</script>

<header>Always at top</header>
{@render children()}
<footer>Always at bottom</footer>
<!-- Content in a specific container -->
<script>
  let { children } = $props();
</script>

<div class="sidebar">Navigation</div>
<main class="content">
  {@render children()}
</main>

A Visual Experiment

Let’s make the layout structure visible. Update your layout:

<!-- 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>

<div style="border: 3px solid blue; padding: 1rem;">
  <p style="color: blue;">LAYOUT START</p>
  
  <div style="border: 3px solid green; padding: 1rem;">
    {@render children()}
  </div>
  
  <p style="color: blue;">LAYOUT END</p>
</div>

Now visit different pages. You’ll see:

  • Blue border around everything (the layout)
  • Green border around page content (where children renders)
  • “LAYOUT START” always at top
  • “LAYOUT END” always at bottom
  • Each page’s content inside the green box

This visual debugging helps you understand exactly where content lands.


What Children Actually Is

In Svelte 5, children is a snippet — a special type that represents renderable content. When SvelteKit prepares to render a route:

  1. It compiles the page into a snippet
  2. It passes that snippet to the layout as children
  3. The layout decides where to render it

You can think of it like a placeholder that gets filled with actual content.


Multiple Render Points?

You might wonder: can you render children multiple times?

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

<!-- Don't do this -->
{@render children()}
<hr>
{@render children()}

Technically this renders the page twice. There’s rarely a good reason to do this — it would show duplicate content and could cause issues with forms or stateful components.

Stick to rendering children exactly once.


The Real Layout

Remove the debug styling and set up a proper structure:

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

<div class="app">
  <!-- Header -->
  <header>
    <!-- Navigation will go here -->
  </header>
  <!-- Main content -->
  <main>
    {@render children()}
  </main>
  
  <!-- Footer -->
  <footer>
    <!-- Footer will go here -->
  </footer>
</div>

This structure provides:

  • A <header> for navigation (top of every page)
  • A <main> for page content (varies per page)
  • A <footer> for site-wide footer (bottom of every page)

Common Mistakes

Rendering Inside a Condition Without Fallback

<!-- ❌ Page might not render -->
{#if showContent}
  {@render children()}
{/if}

If showContent is false, the page content disappears entirely. Be careful with conditional rendering around children.

Nesting Children Incorrectly

<!-- ❌ Confusing structure -->
<div>
  <div>
    <div>
      {@render children()}
    </div>
  </div>
</div>

While this works, excessive nesting makes styling difficult. Keep your layout structure as flat as reasonable.


Summary

The {@render children()} directive is where page content appears within a layout. You control positioning by placing this directive where you want the page to render. The layout wraps, the page fills the designated slot.

Key takeaways:

  • children is a snippet containing the page content
  • {@render children()} renders the page at that exact position
  • Layout structure (header, main, footer) surrounds the render point

Next Steps

The layout structure is ready. Continue with Add a Site Header to build BookIt’s navigation bar.