Skip to content

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.

proptypedefaultrequireddescription
titleComponentProps<typeof Head>["title"]-YesPage title forwarded directly to Head.
descriptionComponentProps<typeof Head>["description"]-YesPage description forwarded directly to Head.
...otherHeadPropsOmit<ComponentProps<typeof Head>, "title" | "description">same as HeadNoAny other Head prop can be passed directly to Layout and is forwarded unchanged.

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>

Uses integration head.* defaults through Head for omitted optional metadata.

Input:

// in astro.config.mjs integration config
eminence({
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>

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>

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.

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.

The component intentionally keeps surface area small: it forwards all received props to Head and controls fixed slot placement in the body.

---
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>