Layouts are special Astro components used to provide a reusable structure for your pages. They typically include common elements like headers, footers, navigation, and meta tags.
What are Layouts?
A layout is just an Astro component that wraps page content using the <slot /> element. There’s nothing special about a layout file—it’s a regular .astro component.
src/layouts/BaseLayout.astro
---
const { title } = Astro . props ;
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title > { title } </ title >
</ head >
< body >
< header >
< nav >
< a href = "/" > Home </ a >
< a href = "/about" > About </ a >
< a href = "/blog" > Blog </ a >
</ nav >
</ header >
< main >
< slot />
</ main >
< footer >
< p > © 2024 My Site </ p >
</ footer >
</ body >
</ html >
Using a Layout
Import and use layouts like any other component:
---
import BaseLayout from '../layouts/BaseLayout.astro' ;
---
< BaseLayout title = "Home Page" >
< h1 > Welcome to my site! </ h1 >
< p > This content appears in the slot. </ p >
</ BaseLayout >
The content between the opening and closing <BaseLayout> tags is passed to the <slot /> in the layout.
Props to Layouts
Pass data to layouts via props:
src/layouts/BlogLayout.astro
---
interface Props {
title : string ;
description : string ;
author : string ;
pubDate : Date ;
}
const { title , description , author , pubDate } = Astro . props ;
const formattedDate = pubDate . toLocaleDateString ();
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "description" content = { description } />
< title > { title } </ title >
</ head >
< body >
< article >
< header >
< h1 > { title } </ h1 >
< p > By { author } on { formattedDate } </ p >
</ header >
< slot />
</ article >
</ body >
</ html >
Use it:
src/pages/blog/my-post.astro
---
import BlogLayout from '../../layouts/BlogLayout.astro' ;
---
< BlogLayout
title = "My First Post"
description = "This is my first blog post"
author = "Alice"
pubDate = {new Date ( '2024-01-01' ) }
>
< p > This is the blog post content. </ p >
< p > It can contain multiple paragraphs. </ p >
</ BlogLayout >
Nesting Layouts
Layouts can import and use other layouts, creating a hierarchy:
src/layouts/BaseLayout.astro
---
const { title } = Astro . props ;
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< title > { title } </ title >
< link rel = "stylesheet" href = "/styles/global.css" />
</ head >
< body >
< slot />
</ body >
</ html >
src/layouts/BlogLayout.astro
---
import BaseLayout from './BaseLayout.astro' ;
interface Props {
title : string ;
author : string ;
}
const { title , author } = Astro . props ;
---
< BaseLayout title = { title } >
< div class = "blog-container" >
< header >
< h1 > { title } </ h1 >
< p > By { author } </ p >
</ header >
< article >
< slot />
</ article >
</ div >
</ BaseLayout >
Now your blog posts automatically get both the blog-specific structure AND the base HTML structure:
src/pages/blog/post.astro
---
import BlogLayout from '../../layouts/BlogLayout.astro' ;
---
< BlogLayout title = "My Post" author = "Alice" >
< p > Post content here. </ p >
</ BlogLayout >
<! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< title > My Post </ title >
< link rel = "stylesheet" href = "/styles/global.css" />
</ head >
< body >
< div class = "blog-container" >
< header >
< h1 > My Post </ h1 >
< p > By Alice </ p >
</ header >
< article >
< p > Post content here. </ p >
</ article >
</ div >
</ body >
</ html >
Layout with Multiple Slots
Use named slots for more complex layouts:
src/layouts/DocumentLayout.astro
---
const { title } = Astro . props ;
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< title > { title } </ title >
< slot name = "head" />
</ head >
< body >
< nav >
< slot name = "nav" />
</ nav >
< main >
< slot />
</ main >
< aside >
< slot name = "sidebar" />
</ aside >
< footer >
< slot name = "footer" >
< p > Default footer content </ p >
</ slot >
</ footer >
</ body >
</ html >
Use named slots:
---
import DocumentLayout from '../layouts/DocumentLayout.astro' ;
---
< DocumentLayout title = "Documentation" >
< link slot = "head" rel = "stylesheet" href = "/docs.css" />
< nav slot = "nav" >
< a href = "/docs" > Docs </ a >
< a href = "/api" > API </ a >
</ nav >
< h1 > Documentation </ h1 >
< p > Main content goes in the default slot. </ p >
< div slot = "sidebar" >
< h3 > Table of Contents </ h3 >
< ul >
< li >< a href = "#intro" > Introduction </ a ></ li >
< li >< a href = "#guide" > Guide </ a ></ li >
</ ul >
</ div >
< p slot = "footer" > Custom footer for this page </ p >
</ DocumentLayout >
Markdown Layouts
Markdown and MDX files can specify a layout in their frontmatter:
---
layout : ../../layouts/BlogLayout.astro
title : My Blog Post
author : Alice
---
# My Blog Post
This is the markdown content.
The layout receives frontmatter data as props:
src/layouts/BlogLayout.astro
---
const { frontmatter } = Astro . props ;
const { title , author } = frontmatter ;
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< title > { title } </ title >
</ head >
< body >
< article >
< h1 > { title } </ h1 >
< p > By { author } </ p >
< slot />
</ article >
</ body >
</ html >
The <slot /> contains the rendered markdown content.
Real-World Example
Here’s a production-ready layout from the Astro source code examples:
src/layouts/BaseLayout.astro
---
interface Props {
title : string ;
description ?: string ;
image ?: string ;
}
const {
title ,
description = 'Default site description' ,
image = '/default-og-image.jpg'
} = Astro . props ;
const canonicalURL = new URL ( Astro . url . pathname , Astro . site );
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< link rel = "canonical" href = { canonicalURL } />
<!-- Primary Meta Tags -->
< title > { title } </ title >
< meta name = "title" content = { title } />
< meta name = "description" content = { description } />
<!-- Open Graph / Facebook -->
< meta property = "og:type" content = "website" />
< meta property = "og:url" content = { Astro . url } />
< meta property = "og:title" content = { title } />
< meta property = "og:description" content = { description } />
< meta property = "og:image" content = {new URL ( image , Astro . url ) } />
<!-- Twitter -->
< meta property = "twitter:card" content = "summary_large_image" />
< meta property = "twitter:url" content = { Astro . url } />
< meta property = "twitter:title" content = { title } />
< meta property = "twitter:description" content = { description } />
< meta property = "twitter:image" content = {new URL ( image , Astro . url ) } />
< slot name = "head" />
</ head >
< body >
< header >
< nav >
< a href = "/" > Home </ a >
< a href = "/about" > About </ a >
< a href = "/blog" > Blog </ a >
</ nav >
</ header >
< main >
< slot />
</ main >
< footer >
< p > © {new Date (). getFullYear () } My Site </ p >
</ footer >
</ body >
</ html >
Layout Patterns
Base Layout
App Layout
Content Layout
---
// Minimal HTML structure
const { title } = Astro . props ;
---
< ! DOCTYPE html >
< html >
< head >
< title > { title } </ title >
</ head >
< body >
< slot />
</ body >
</ html >
---
// Adds nav and footer
import BaseLayout from './BaseLayout.astro' ;
---
< BaseLayout >
< nav >< slot name = "nav" /></ nav >
< main >< slot /></ main >
< footer >< slot name = "footer" /></ footer >
</ BaseLayout >
---
// Adds article structure
import AppLayout from './AppLayout.astro' ;
const { frontmatter } = Astro . props ;
---
< AppLayout >
< article >
< h1 > { frontmatter . title } </ h1 >
< slot />
</ article >
</ AppLayout >
Dynamic Layouts
Conditionally render different layouts:
src/layouts/ConditionalLayout.astro
---
import LightLayout from './LightLayout.astro' ;
import DarkLayout from './DarkLayout.astro' ;
const { theme = 'light' , title } = Astro . props ;
const Layout = theme === 'dark' ? DarkLayout : LightLayout ;
---
< Layout title = { title } >
< slot />
</ Layout >
Best Practices
Start with a base layout
Create a minimal base layout with essential HTML structure.
Create specialized layouts
Build specialized layouts that extend the base for different page types.
Use TypeScript
Type your layout props for better developer experience.
Keep layouts focused
Each layout should serve a specific purpose (blog, docs, landing page).
Use slots wisely
Named slots provide flexibility, but don’t overuse them.
Prefer composition Nest layouts instead of duplicating code.
Default props Provide sensible defaults for optional props.
SEO metadata Include SEO tags in your base layout.
Consistent structure Keep similar pages using the same layout.
Learn More
Components Learn about Astro component basics
Content Collections Type-safe content with automatic layouts