Skip to main content
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>&copy; 2024 My Site</p>
    </footer>
  </body>
</html>

Using a Layout

Import and use layouts like any other component:
src/pages/index.astro
---
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:
src/pages/docs.astro
---
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:
src/pages/blog/post.md
---
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>&copy; {new Date().getFullYear()} My Site</p>
    </footer>
  </body>
</html>

Layout Patterns

---
// Minimal HTML structure
const { title } = Astro.props;
---
<!DOCTYPE html>
<html>
  <head>
    <title>{title}</title>
  </head>
  <body>
    <slot />
  </body>
</html>

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

1

Start with a base layout

Create a minimal base layout with essential HTML structure.
2

Create specialized layouts

Build specialized layouts that extend the base for different page types.
3

Use TypeScript

Type your layout props for better developer experience.
4

Keep layouts focused

Each layout should serve a specific purpose (blog, docs, landing page).
5

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

Build docs developers (and LLMs) love