Skip to content

Layouts

Layouts are Astro components used to provide a reusable UI structure, such as a page template.

We conventionally use the term “layout” for Astro components that provide common UI elements shared across pages such as headers, navigation bars, and footers. A typical Astro layout component provides Astro, Markdown or MDX pages with:

  • a page shell (<html>, <head> and <body> tags)
  • a <slot /> to specify where individual page content should be injected.

But, there is nothing special about a layout component! They can accept props and import and use other components like any other Astro component. They can include UI frameworks components and client-side scripts. They do not even have to provide a full page shell, and can instead be used as partial UI templates.

However, if a layout component does contain a page shell, its <html> element must be the parent of all other elements in the component. All <style> or <script> elements must be enclosed by the <html> tags.

Layout components are commonly placed in a src/layouts directory in your project for organization, but this is not a requirement; you can choose to place them anywhere in your project. You can even colocate layout components alongside your pages by prefixing the layout names with _.

src/layouts/MySiteLayout.astro
---
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<BaseHead title={title}/>
</head>
<body>
<nav>
<a href="#">Home</a>
<a href="#">Posts</a>
<a href="#">Contact</a>
</nav>
<h1>{title}</h1>
<article>
<slot /> <!-- your content is injected here -->
</article>
<Footer />
</body>
<style>
h1 {
font-size: 2rem;
}
</style>
</html>
src/pages/index.astro
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Home Page">
<p>My page content, wrapped in a layout!</p>
</MySiteLayout>
Learn more about slots.

Page layouts are especially useful for Markdown and MDX pages which otherwise would not have any page formatting.

Astro provides a special layout frontmatter property to specify which .astro component to use as the page layout.

src/pages/page.md
---
layout: ../layouts/BaseLayout.astro
title: "Hello, World!"
author: "Matthew Phillips"
date: "09 Aug 2022"
---
All frontmatter properties are available as props to an Astro layout component.
The `layout` property is the only special one provided by Astro.
You can use it in both Markdown and MDX files located within `src/pages/`.

A typical layout for Markdown or MDX pages includes:

  1. The frontmatter prop to access the Markdown or MDX page’s frontmatter and other data.
  2. A default <slot /> to indicate where the page’s Markdown/MDX content should be rendered.
src/layouts/BaseLayout.astro
---
// 1. The frontmatter prop gives access to frontmatter and other data
const { frontmatter } = Astro.props;
---
<html>
<head>
<!-- Add other Head elements here, like styles and meta tags. -->
<title>{frontmatter.title}</title>
</head>
<body>
<!-- Add other UI components here, like common headers and footers. -->
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<!-- 2. Rendered HTML will be passed into the default slot. -->
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>

You can set a layout’s Props type with the MarkdownLayoutProps or MDXLayoutProps helper:

src/layouts/BaseLayout.astro
---
import type { MarkdownLayoutProps } from 'astro';
type Props = MarkdownLayoutProps<{
// Define frontmatter props here
title: string;
author: string;
date: string;
}>;
// Now, `frontmatter`, `url`, and other Markdown layout properties
// are accessible with type safety
const { frontmatter, url } = Astro.props;
---
<html>
<head>
<link rel="canonical" href={new URL(url, Astro.site).pathname}>
<title>{frontmatter.title}</title>
</head>
<body>
<h1>{frontmatter.title} by {frontmatter.author}</h1>
<slot />
<p>Written on: {frontmatter.date}</p>
</body>
</html>

A Markdown/MDX layout will have access to the following information via Astro.props:

  • file - The absolute path of this file (e.g. /home/user/projects/.../file.md).
  • url - If it’s a page, the URL of the page (e.g. /en/guides/markdown-content).
  • frontmatter - all frontmatter from the Markdown or MDX document.
    • frontmatter.file - The same as the top-level file property.
    • frontmatter.url - The same as the top-level url property.
  • headings - A list of headings (h1 -> h6) in the Markdown or MDX document with associated metadata. This list follows the type: { depth: number; slug: string; text: string }[].
  • (Markdown only) rawContent() - A function that returns the raw Markdown document as a string.
  • (Markdown only) compiledContent() - A function that returns the Markdown document compiled to an HTML string.

An example Markdown blog post may pass the following Astro.props object to its layout:

Astro.props = {
file: "/home/user/projects/.../file.md",
url: "/en/guides/markdown-content/",
frontmatter: {
/** Frontmatter from a blog post */
title: "Astro 0.18 Release",
date: "Tuesday, July 27 2021",
author: "Matthew Phillips",
description: "Astro 0.18 is our biggest release since Astro launch.",
/** Generated values */
file: "/home/user/projects/.../file.md",
url: "/en/guides/markdown-content/"
},
headings: [
{
"depth": 1,
"text": "Astro 0.18 Release",
"slug": "astro-018-release"
},
{
"depth": 2,
"text": "Responsive partial hydration",
"slug": "responsive-partial-hydration"
}
/* ... */
],
/** Available in Markdown only */
rawContent: () => "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]",
compiledContent: () => "<h1>Astro 0.18 Release</h1>\n<p>A little over a month ago, the first public beta [...]</p>",
}

Importing Layouts Manually (MDX)

Section titled Importing Layouts Manually (MDX)

You may need to pass information to your MDX layout that does not (or cannot) exist in your frontmatter. In this case, you can instead import and use a <Layout /> component and pass it props like any other component:

src/pages/posts/first-post.mdx
---
layout: ../../layouts/BaseLayout.astro
title: 'My first MDX post'
publishDate: '21 September 2022'
---
import BaseLayout from '../../layouts/BaseLayout.astro';
export function fancyJsHelper() {
return "Try doing that with YAML!";
}
<BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>
Welcome to my new Astro blog, using MDX!
</BaseLayout>

Then, your values are available to you through Astro.props in your layout, and your MDX content will be injected into the page where your <slot /> component is written:

src/layouts/BaseLayout.astro
---
const { title, fancyJsHelper } = Astro.props;
---
<!-- -->
<h1>{title}</h1>
<slot /> <!-- your content is injected here -->
<p>{fancyJsHelper()}</p>
<!-- -->
Learn more about Astro’s Markdown and MDX support in our Markdown/MDX guide.

Using one Layout for .md, .mdx, and .astro

Section titled Using one Layout for .md, .mdx, and .astro

A single Astro layout can be written to receive the frontmatter object from .md and .mdx files, as well as any named props passed from .astro files.

In the example below, the layout will display the page title either from a frontmatter YAML title property or from an Astro component passing a title attribute:

src/components/MyLayout.astro
---
const { title } = Astro.props.frontmatter || Astro.props;
---
<html>
<head></head>
<body>
<h1>{title}</h1>
<slot />
</body>
</html>

Layout components do not need to contain an entire page worth of HTML. You can break your layouts into smaller components, and combine layout components to create even more flexible, page templates. This pattern is useful when you want to share some code across multiple layouts.

For example, a BlogPostLayout.astro layout component could style a post’s title, date and author. Then, a site-wide BaseLayout.astro could handle the rest of your page template, like navigation, footers, SEO meta tags, global styles, and fonts. You can also pass props received from your post to another layout, just like any other nested component.

src/layouts/BlogPostLayout.astro
---
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
---
<BaseLayout url={frontmatter.url}>
<h1>{frontmatter.title}</h1>
<h2>Post author: {frontmatter.author}</h2>
<slot />
</BaseLayout>