Layout
Layout renders a complete HTML document scaffold (<html> and <body>) and uses Head for metadata. It provides three named slots (head, body-start, body-end) and the default slot for main body content.
| prop | type | default | required | description |
|---|---|---|---|---|
title | ComponentProps<typeof Head>["title"] | - | Yes | Page title forwarded directly to Head. |
description | ComponentProps<typeof Head>["description"] | - | Yes | Page description forwarded directly to Head. |
...otherHeadProps | Omit<ComponentProps<typeof Head>, "title" | "description"> | same as Head | No | Any other Head prop can be passed directly to Layout and is forwarded unchanged. |
Examples
Section titled “Examples”Input:
<Layout title="Home" description="Home page"> <main>Welcome</main></Layout>Output:
<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Home</title> <meta name="description" content="Home page" /> <meta name="generator" content="Astro v6.1.1" /> </head> <body> <main>Welcome</main> </body></html>Automatic
Section titled “Automatic”Uses integration head.* defaults through Head for omitted optional metadata.
Input:
// in astro.config.mjs integration configeminence({ headTags: { titleTemplate: "%s | My Site", colorScheme: "dark", },});<Layout title="Home" description="Home page"> <main>Welcome</main></Layout>Output:
<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Home | My Site</title> <meta name="color-scheme" content="dark" /> <meta name="description" content="Home page" /> <meta name="generator" content="Astro v6.1.1" /> </head> <body> <main>Welcome</main> </body></html>Complete
Section titled “Complete”Input:
<Layout title="Home" titleTemplate="%s | Example" description="Welcome to Example" canonical="https://example.com/" themeColor={{ light: "#ffffff", dark: "#111111" }}> <meta slot="head" name="robots" content="index, follow" /> <div slot="body-start">Banner</div> <main>Welcome</main> <script slot="body-end" src="/app.js"></script></Layout>Output:
<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Home | Example</title> <meta name="robots" content="index, follow" /> <link rel="canonical" href="https://example.com/" /> <meta name="description" content="Welcome to Example" /> <meta name="generator" content="Astro v6.1.1" /> <meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff" /> <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#111111" /> </head> <body> <div>Banner</div> <main>Welcome</main> <script src="/app.js"></script> </body></html>Decisions Made
Section titled “Decisions Made”Layout delegates metadata behavior entirely to Head
Section titled “Layout delegates metadata behavior entirely to Head”Layout does not reimplement head-tag logic. All metadata defaults, fallbacks, and suppression behavior are defined by Head and forwarded via direct props.
Slot placement is deterministic
Section titled “Slot placement is deterministic”The head slot is rendered inside Head where custom page-level tags belong. In the body, body-start renders before the default slot and body-end renders after it.
Layout only owns slot composition
Section titled “Layout only owns slot composition”The component intentionally keeps surface area small: it forwards all received props to Head and controls fixed slot placement in the body.
Source code
Section titled “Source code”---import { Head, type HeadProps } from ".";
interface Props extends HeadProps {}---
<!doctype html><html> <Head {...Astro.props}> <slot name="head" /> </Head> <body> <slot name="body-start" /> <slot /> <slot name="body-end" /> </body></html>