Svelte and SvelteKit course Β· Module 11: SvelteKit Routing

Layouts

2 min read Β·
In this lesson4

+layout.svelte

<!-- src/routes/+layout.svelte -->
<script>
  import Header from '#lib/components/Header.svelte';
  import Footer from '#lib/components/Footer.svelte';

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

<Header />

<main>
  {@render children()}  <!-- The page renders here -->
</main>

<Footer />

<style>
  main {
    max-width: 1200px;
    margin: 0 auto;
    padding: 1rem;
  }
</style>

The layout receives the page content as the children snippet and shows it with the {@render children()} tag. In Svelte 4 the <slot /> element did this job; in Svelte 5 it is deprecated.

The #lib alias is the SvelteKit 3 successor of $lib: it points to the src/lib folder (an imports entry in package.json), and you import JS modules with their extension, for example #lib/utils.js.

Nested layouts

src/routes/
β”œβ”€β”€ +layout.svelte           # Main layout
β”œβ”€β”€ +page.svelte             # Home page
└── dashboard/
    β”œβ”€β”€ +layout.svelte       # Layout for dashboard (nested)
    β”œβ”€β”€ +page.svelte         # /dashboard
    └── settings/
        └── +page.svelte     # /dashboard/settings
<!-- src/routes/dashboard/+layout.svelte -->
<script>
  import Sidebar from './Sidebar.svelte';

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

<div class="dashboard-layout">
  <Sidebar />
  <div class="content">
    {@render children()}
  </div>
</div>

Layout data

// src/routes/+layout.js
export function load() {
  return {
    user: { name: 'Ragnar' }
  };
}
<!-- src/routes/+layout.svelte -->
<script>
  let { data, children } = $props();
</script>

<nav>
  Logged in as: {data.user.name}
</nav>

{@render children()}

Resetting layout

<!-- src/routes/fullscreen/+page@.svelte -->
<!-- @ resets to root layout -->
<h1>Fullscreen page without layout</h1>
Code for this lesson: App.svelte
<script>
  // Layout and nested layouts

  let showSidebar = $state(true);
</script>

<div class="app-layout">
  <!-- +layout.svelte (main) -->
  <header class="header">
    <h1>Viking App</h1>
    <nav>
      <a href="/">Home</a>
      <a href="/warriors">Warriors</a>
      <a href="/ships">Ships</a>
    </nav>
  </header>

  <div class="main-content">
    {#if showSidebar}
      <!-- sidebar from (warriors)/+layout.svelte -->
      <aside class="sidebar">
        <h3>Warrior filter</h3>
        <ul>
          <li><a href="/warriors?status=all">All</a></li>
          <li><a href="/warriors?status=alive">Alive</a></li>
          <li><a href="/warriors?status=dead">Walhalla</a></li>
        </ul>
      </aside>
    {/if}

    <!-- +page.svelte -->
    <main class="page">
      <button onclick={() => showSidebar = !showSidebar}>
        {showSidebar ? 'Hide' : 'Show'} sidebar
      </button>

      <h2>Page content</h2>
      <p>This is where the layout renders the page: <code>{'{@render children()}'}</code></p>

      <div class="structure">
        <pre>
src/routes/
β”œβ”€β”€ +layout.svelte        ← Header (shared)
β”œβ”€β”€ +page.svelte          ← Home
└── warriors/
    β”œβ”€β”€ +layout.svelte    ← Sidebar (for warriors)
    └── +page.svelte      ← Warrior list
        </pre>
      </div>
    </main>
  </div>

  <!-- Footer from main layout -->
  <footer class="footer">
    <p>Β© 2024 Viking App - Powered by SvelteKit</p>
  </footer>
</div>

<style>
  .app-layout {
    font-family: 'Georgia', serif;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: #0a1628;
    color: #b8d4f7;
  }

  .header {
    background: #1e3a5f;
    padding: 1rem 2rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .header h1 {
    margin: 0;
    color: #ffd700;
    font-size: 1.5rem;
  }

  .header nav {
    display: flex;
    gap: 1rem;
  }

  .header a {
    color: #b8d4f7;
    text-decoration: none;
  }

  .main-content {
    flex: 1;
    display: flex;
  }

  .sidebar {
    width: 200px;
    background: rgba(74, 144, 217, 0.1);
    padding: 1rem;
    border-right: 1px solid #4a90d9;
  }

  .sidebar h3 {
    color: #4a90d9;
    font-size: 1rem;
    margin-top: 0;
  }

  .sidebar ul {
    list-style: none;
    padding: 0;
  }

  .sidebar li {
    margin: 0.5rem 0;
  }

  .sidebar a {
    color: #b8d4f7;
    text-decoration: none;
  }

  .page {
    flex: 1;
    padding: 2rem;
  }

  button {
    padding: 0.5rem 1rem;
    background: #1e3a5f;
    color: #b8d4f7;
    border: 1px solid #4a90d9;
    border-radius: 4px;
    cursor: pointer;
    margin-bottom: 1rem;
  }

  h2 {
    color: #4a90d9;
  }

  code {
    background: rgba(0,0,0,0.5);
    padding: 0.2rem 0.5rem;
    border-radius: 4px;
  }

  .structure pre {
    background: rgba(0,0,0,0.5);
    padding: 1rem;
    border-radius: 4px;
    font-size: 0.85rem;
  }

  .footer {
    background: #1e3a5f;
    padding: 1rem;
    text-align: center;
    font-size: 0.9rem;
  }
</style>

Docs entries used in this lesson

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. How do you show the page content inside +layout.svelte (Svelte 5)?

Useful articles