---
title: "Astro Layouts"
description: "Template inheritance — wrap pages in shared structure without repeating yourself."
kind: guide
maturity: evergreen
confidence: high
origin: ai-drafted
author: "Agent"
directedBy: "krow"
tags: [astro, fundamentals]
published: 2026-03-15
modified: 2026-06-13
wordCount: 1239
readingTime: 6
series: "learn-astro"
series_order: 5
prerequisites: [astro-components]
url: https://krowdev.com/guide/astro-layouts/
---
## Agent Context

- Canonical: https://krowdev.com/guide/astro-layouts/
- Markdown: https://krowdev.com/guide/astro-layouts.md
- Full corpus: https://krowdev.com/llms-full.txt
- Kind: guide
- Maturity: evergreen
- Confidence: high
- Origin: ai-drafted
- Author: Agent
- Directed by: krow
- Published: 2026-03-15
- Modified: 2026-06-13
- Words: 1239 (6 min read)
- Tags: astro, fundamentals
- Series: learn-astro (#5)
- Prerequisites: astro-components
- Content map:
  - h2: Layouts Are Components That Wrap Pages
  - h2: A Minimal Layout
  - h2: Layout Composition (Extending)
  - h2: Real Example: The Full Layout Chain
  - h2: How Pages Use Layouts
  - h2: Layouts vs. Components
  - h2: Sources
- Diagrams: Mermaid fences are paired with adjacent ASCII companions in this document (1 Mermaid, 1 ASCII); HTML figures expose rendered SVG plus copyable Mermaid/ASCII source tabs.
- Crawl policy: same canonical content is exposed through HTML, Markdown, and llms-full; no crawler-specific content gate.

## Layouts Are Components That Wrap Pages

Layouts are a specific pattern on top of [components](/guide/astro-components/). Once you have layouts down, move on to [content collections](/guide/astro-content-collections/) and [styling](/guide/astro-styling/). For the wider context start at the [mental model](/guide/astro-mental-model/).

A layout is just a component that provides the shared structure (html tag, head, header, footer) and uses `<slot />` for page-specific content.

:::analogy
**LaTeX:** A `documentclass` defines margins, fonts, header/footer. Your content goes in `\begin{document}...\end{document}`.

**Astro:** A layout defines `<html>`, `<head>`, header, footer. Your content goes in `<slot />`.

```latex
\documentclass{article}     ←  Layout
\begin{document}
  Your content here          ←  <slot />
\end{document}
```
:::

## A Minimal Layout

```astro
---
// src/layouts/Base.astro
interface Props {
  title: string;
}
const { title } = Astro.props;
---

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>{title}</title>
  </head>
  <body>
    <header>krowdev</header>
    <main>
      <slot />  <!-- page content injected here -->
    </main>
    <footer>&copy; 2026</footer>
  </body>
</html>
```

Using it:
```astro
---
// src/pages/about.astro
import Base from '../layouts/Base.astro';
---

<Base title="About">
  <h1>About</h1>
  <p>This replaces <slot /> in Base.astro</p>
</Base>
```

## Layout Composition (Extending)

Layouts can wrap other layouts. krowdev uses a two-layer system:

```mermaid
graph TD
  accTitle: krowdev's two-layer layout system
  accDescr: Base.astro provides the html shell, header, a content slot, and footer. KBEntry wraps Base, adding a three-column grid and its own slot for article markdown.
  base["Base.astro"]
  base --> head1["&lt;html&gt;, &lt;head&gt;, meta tags"]
  base --> header["Header (nav, logo, theme toggle)"]
  base --> bslot["&lt;slot /&gt; — KBEntry fills this"]
  base --> footer["Footer"]
  kb["KBEntry.astro (uses Base)"]
  kb --> callbase["Calls &lt;Base title={title}&gt;"]
  kb --> grid["3-column grid: sidebar | content | ToC"]
  kb --> kbslot["&lt;slot /&gt; — article markdown fills this"]
```
```ascii
Base.astro
├── <html>, <head>, meta tags
├── Header (nav, logo, theme toggle)
├── <slot />  ← KBEntry.astro fills this
└── Footer

KBEntry.astro (uses Base)
├── Calls <Base title={title}>
├── 3-column grid: sidebar | content | ToC
└── <slot />  ← actual article markdown fills this
```

```astro
---
// src/layouts/KBEntry.astro
import Base from './Base.astro';
import SeriesSidebar from '../components/SeriesSidebar.astro';
import TableOfContents from '../components/TableOfContents.astro';

const { title, headings } = Astro.props;
---

<Base title={title}>
  <SeriesSidebar />
    <article>
      <h1>{title}</h1>
      <slot />   <!-- article content -->
    </article>
    <TableOfContents headings={headings} />
  
</Base>
```

:::key
The chain is: **Page → Layout → Layout → HTML**. Each layer adds structure and passes content down through `<slot />`. No duplication — the `<html>` tag, header, and footer exist in exactly one file (`Base.astro`).
:::

## Real Example: The Full Layout Chain

This is the exact code that renders the page you're reading right now. Toggle to see how 3 files compose into one page:

<p class="cv-label">KB entry layout chain</p>
<nav class="cv-tabs">
<button class="cv-tab active" data-tab="source">Source (3 files)</button>
<button class="cv-tab" data-tab="compiled">Compiled HTML</button>
<button class="cv-tab" data-tab="rendered">What You See</button>
</nav>
<div class="cv-panel active" data-panel="source">

**1. Page** — `src/pages/[kind]/[...slug].astro`
```astro
---
import { getCollection, render } from 'astro:content';
import KBEntry from '../../layouts/KBEntry.astro';

export async function getStaticPaths() {
  const entries = await getCollection('kb');
  return entries.map(entry => ({
    params: { kind: entry.data.kind, slug: entry.id },
    props: { entry },
  }));
}

const { entry } = Astro.props;
const { Content, headings } = await render(entry);
---

<KBEntry title={entry.data.title} headings={headings}>
  <Content />
</KBEntry>
```

**2. Layout** — `src/layouts/KBEntry.astro` (wraps the page)
```astro
---
import Base from './Base.astro';
import SeriesSidebar from '../components/SeriesSidebar.astro';
import TableOfContents from '../components/TableOfContents.astro';

const { title, headings } = Astro.props;
---

<Base title={title}>
  <SeriesSidebar />
    <article data-pagefind-body>
      <h1>{title}</h1>
      <slot />
    </article>
    <TableOfContents headings={headings} />
    
  
</Base>
```

**3. Root Layout** — `src/layouts/Base.astro` (wraps everything)
```astro
---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import '../styles/global.css';

const { title } = Astro.props;
---

<!doctype html>
<html lang="en" data-theme="dark">
  <head>
    <title>{title} — krowdev</title>
    <!-- meta tags, fonts, etc -->
  </head>
  <body>
    <Header />
    <main id="main">
      <slot />
    </main>
    <Footer />
  </body>
</html>
```

<div class="cv-panel" data-panel="compiled">

```html
<!-- Final compiled output (simplified) -->
<!doctype html>
<html lang="en" data-theme="dark">
  <head>
    <title>Layouts — krowdev</title>
    <meta name="description" content="Template inheritance..." />
    <link rel="stylesheet" href="/_astro/global.css" />
    <!-- font preloads -->
  </head>
  <body>
    <!-- Header component (expanded) -->
    <header class="header">
      <nav class="header-inner">
        <a href="/" class="logo">...</a>
        <a href="/explore/" class="nav-link active">Explore</a>
        <a href="/guide/..." class="nav-link">Guides</a>
        <button class="theme-toggle">...</button>
      </nav>
    </header>

    <main id="main">
      <!-- SeriesSidebar component (expanded) -->
        <nav class="series-sidebar">
          <p class="section-label">Learn Astro 6</p>
            <ul>
              <li><a href="/guide/astro-mental-model/">The Mental Model</a></li>
              <!-- ... 8 more lessons ... -->
            </ul>
          
        </nav>

        <!-- Article content (markdown → HTML) -->
        <article data-pagefind-body>
          <h1>Layouts</h1>
          <p>Template inheritance — wrap pages in shared structure...</p>
          <h2 id="layouts-are-components">Layouts Are Components...</h2>
          <!-- rest of article -->
        </article>

        <!-- TableOfContents component (expanded) -->
        <aside class="toc">
          <p class="toc-title">On this page</p>
          <ul>
            <li><a href="#layouts-are-components">Layouts Are Components</a></li>
            <!-- ... more headings ... -->
          </ul>
        </aside>
      
    </main>

    <footer class="footer">...</footer>
  </body>
</html>
```

<div class="cv-panel" data-panel="rendered">

The three source files compose into a single HTML page:

- **Base.astro** provided: `<html>`, `<head>`, Header, Footer
- **KBEntry.astro** provided: 3-column grid, Sidebar, ToC
- **[...slug].astro** provided: the rendered markdown content

The `<slot />` in each layout was replaced by the content from the layer above:
1. Markdown → rendered HTML → placed in KBEntry's `<slot />`
2. KBEntry's output → placed in Base's `<slot />`
3. Base wraps everything in `<html>` → final page

**No duplication.** The header exists in one file. The sidebar exists in one file. The grid layout exists in one file. Change any of them and every kb entry updates.

## How Pages Use Layouts

A page component wraps its content in a layout:

```astro
---
// src/pages/[kind]/[...slug].astro
import KBEntry from '../../layouts/KBEntry.astro';

const { entry } = Astro.props;
const { Content, headings } = await render(entry);
---

<KBEntry title={entry.data.title} headings={headings}>
  <Content />   <!-- markdown rendered to HTML, placed in KBEntry's <slot /> -->
</KBEntry>
```

The flow:
1. `[...slug].astro` renders the markdown and passes it to `KBEntry`
2. `KBEntry.astro` places it in a 3-column grid and passes everything to `Base`
3. `Base.astro` wraps it all in `<html>` with header and footer

## Layouts vs. Components

| | Layout | Component |
|---|---|---|
| **Purpose** | Wraps entire pages | Reusable piece within a page |
| **Uses `<slot />`?** | Always | Sometimes |
| **Lives in** | `src/layouts/` | `src/components/` |
| **Provides `<html>`?** | Usually (root layout) | Never |
| **Convention** | Named after content type (KBEntry, Base) | Named after what it renders (Header, Badge) |

Technically they're both `.astro` files with the same syntax. The distinction is organizational — layouts provide page structure, components provide reusable pieces.

**Challenge: Trace the layout chain**

For the page you're reading right now (`/guide/astro-layouts/`), trace the full chain:

1. Which **page file** generates this URL?
2. Which **layout** does that page use?
3. Which **layout** does *that* layout use?
4. Which **components** appear at each level?

Read the actual files to confirm:
```bash
cat src/pages/\[kind\]/\[...slug\].astro
cat src/layouts/KBEntry.astro
cat src/layouts/Base.astro
```

**Answer**

1. `src/pages/[kind]/[...slug].astro` — dynamic route, one page per kb entry
2. Uses `KBEntry.astro` — adds sidebar + content + ToC grid
3. `KBEntry.astro` uses `Base.astro` — adds `<html>`, `<head>`, Header, Footer
4. Components:
   - **Base level:** Header (→ ThemeToggle), Footer
   - **KBEntry level:** SeriesSidebar, TableOfContents
   - **Page level:** None (just rendered markdown)

---
Previous: [Components](/guide/astro-components/) | Next: [Content Collections](/guide/astro-content-collections/)

## Sources

- Astro Docs, [Layouts](https://docs.astro.build/en/basics/layouts/)
- Astro Docs, [Slots](https://docs.astro.build/en/basics/astro-components/#slots)
- Astro Docs, [Markdown layouts](https://docs.astro.build/en/guides/markdown-content/#frontmatter-layout-property)