Agent context packet

Structured metadata, source alternates, graph links, headings, series position, and diagram inventory for crawlers and agent readers.

Table of contents

  1. Layouts Are Components That Wrap Pages
  2. A Minimal Layout
  3. Layout Composition (Extending)
  4. Real Example: The Full Layout Chain
  5. How Pages Use Layouts
  6. Layouts vs. Components
  7. Sources

Series context

Learn Astro 6

A ground-up guide to Astro for developers coming from Python, LaTeX, or scientific computing.

  1. The Mental Model
  2. File-Based Routing
  3. .astro Files
  4. Components
  5. Astro Layouts
  6. Content Collections
  7. Astro Styling
  8. Markdown and Code Blocks
  9. Build and Deploy

Entry facts

Kind
guide
Maturity
evergreen
Confidence
high
Origin
ai-drafted (AI-drafted, human-reviewed)
Author
Agent
Directed by
krow
Published
Modified
Words
1,239 (6 min read)
Series
learn-astro #5
Tags
astro, fundamentals
Prerequisites
Full corpus
/llms-full.txt
Readable corpus
/source/full-corpus/

Graph links

Prerequisites astro-components

Tagsastro, fundamentals

Diagram inventory

  1. Layout Composition (Extending) Mermaid SVG, Mermaid source, ASCII companion

Astro Layouts

Template inheritance — wrap pages in shared structure without repeating yourself.

/ directed by / / 6 min read
On this page

Layouts Are Components That Wrap Pages

Layouts are a specific pattern on top of components. Once you have layouts down, move on to content collections and styling. For the wider context start at the 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 />.

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

A Minimal Layout

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:

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:

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.

krowdev's two-layer layout systemBase.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.astro

<html>, <head>, meta tags

Header (nav, logo, theme toggle)

<slot /> — KBEntry fills this

Footer

KBEntry.astro (uses Base)

Calls <Base title={title}>

3-column grid: sidebar | content | ToC

<slot /> — article markdown fills this

krowdev's two-layer layout system
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}>
<div class="kb-layout">
<SeriesSidebar />
<article>
<h1>{title}</h1>
<slot /> <!-- article content -->
</article>
<TableOfContents headings={headings} />
</div>
</Base>
Key Insight

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:

KB entry layout chain

1. Pagesrc/pages/[kind]/[...slug].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. Layoutsrc/layouts/KBEntry.astro (wraps the page)

---
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}>
<div class="kb-layout">
<SeriesSidebar />
<article data-pagefind-body>
<h1>{title}</h1>
<slot />
</article>
<div class="toc-column">
<TableOfContents headings={headings} />
</div>
</div>
</Base>

3. Root Layoutsrc/layouts/Base.astro (wraps everything)

---
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>
<!-- 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">
<div class="kb-layout">
<!-- SeriesSidebar component (expanded) -->
<nav class="series-sidebar">
<div class="sidebar-section">
<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>
</div>
</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>
</div>
</main>
<footer class="footer">...</footer>
</body>
</html>

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:

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

LayoutComponent
PurposeWraps entire pagesReusable piece within a page
Uses <slot />?AlwaysSometimes
Lives insrc/layouts/src/components/
Provides <html>?Usually (root layout)Never
ConventionNamed 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:

Terminal window
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)

Sources

Diagram

Drag to pan · scroll or pinch to zoom · Esc to close