Markdown source
Astro Layouts Markdown source
Readable source view for humans. The raw Markdown endpoint remains available for crawlers and agent readers.
---
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>© 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["<html>, <head>, meta tags"]
base --> header["Header (nav, logo, theme toggle)"]
base --> bslot["<slot /> — KBEntry fills this"]
base --> footer["Footer"]
kb["KBEntry.astro (uses Base)"]
kb --> callbase["Calls <Base title={title}>"]
kb --> grid["3-column grid: sidebar | content | ToC"]
kb --> kbslot["<slot /> — 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)