Building composable layouts in Astro

By

Learn how to build composable layouts in Astro, extracting shared HTML like the header and footer into a src/layouts file that wraps pages with a slot.

~~~

When a site grows past one page, shared HTML starts to duplicate. Every page repeats the doctype, the <head>, the navigation, the footer. Then you change something in the header and you are editing five files, hoping you caught them all.

A layout fixes this. A layout is an Astro component that owns the shared page structure, while each route supplies its own content. In other tools, like Hugo, you would use partials. In Astro it’s all components, and by convention layouts live in src/layouts/.

This works the same in Astro 7.

Here is src/layouts/Layout.astro:

---
interface Props {
  title: string
  description: string
}

const { title, description } = Astro.props
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="description" content={description} />
    <title>{title}</title>
  </head>
  <body>
    <nav><a href="/">Home</a></nav>
    <main><slot /></main>
    <footer>Built with Astro</footer>
  </body>
</html>

The Props interface lists the values the layout expects, and Astro.props hands them over.

The <slot /> element marks where the page content goes. A page imports the layout and uses it like any other component:

---
import Layout from "../layouts/Layout.astro"
---

<Layout title="About" description="Learn who built this site">
  <h1>About</h1>
  <p>This content fills the layout's slot.</p>
</Layout>

Everything between <Layout> and </Layout> replaces the slot. The props fill the metadata.

The useful part is how the work gets split. Each page knows its own title and description, so it stays responsible for accurate metadata. The layout makes sure every page comes out as a valid, consistent document, and it’s the one place that owns the doctype, the lang attribute, the navigation and the footer.

Layouts are components, so they compose. A blog can create a PostLayout that imports the base Layout, adds the post header inside it, and passes title and description through. Each page then wraps itself in the most specific layout it needs.

Like other Astro components, the layout runs while rendering. It doesn’t stay alive in the browser, so it adds no client-side JavaScript to the page.

A classic mistake is forgetting <slot />. The layout renders fine, and every page using it comes out with an empty <main>. If page content silently disappears inside a correct shell, check the layout for a missing slot before you debug the page.

To check your setup, run the site and open two pages. They share the same navigation and footer, and each one shows its own title, description and main content.

Practice the composition step in the nested-layout validation exercise, which checks metadata, missing props and both layout slots.

Tagged: Astro · All topics

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about astro: