Layouts
In Next.js App Router you create a layout.tsx next to your pages and React automatically wraps children through it. In Astro there is no automatic layout wrapping — you create a layout component in src/layouts/ and explicitly import and use it inside each page. The layout receives page content through <slot />, which is Astro’s equivalent of React’s children prop.
Layouts require a multi-file project to observe — run these examples in a full Astro project (
npm create astro@latest) locally.
Defining a layout
Section titled “Defining a layout”// app/layout.tsx (Next.js App Router)export default function RootLayout({ children,}: { children: React.ReactNode;}) { return ( <html lang="en"> <head> <title>My Site</title> </head> <body> <nav>My Nav</nav> <main>{children}</main> <footer>My Footer</footer> </body> </html> );}---// src/layouts/Layout.astrointerface Props { title?: string;}const { title = 'My Site' } = Astro.props;---<html lang="en"> <head> <meta charset="utf-8" /> <title>{title}</title> </head> <body> <nav>My Nav</nav> <main> <slot /> <!-- page content goes here --> </main> <footer>My Footer</footer> </body></html>Using the layout in a page
Section titled “Using the layout in a page”// app/about/page.tsx (Next.js App Router)// layout.tsx wraps this automatically — nothing to importexport default function AboutPage() { return <h1>About</h1>;}
// -----------------------------------------// pages/_app.tsx (Next.js Pages Router)// import type { AppProps } from 'next/app';// export default function App({ Component, pageProps }: AppProps) {// return <Layout><Component {...pageProps} /></Layout>;// }---// src/pages/about.astro// Layouts must be explicitly imported in every pageimport Layout from '../layouts/Layout.astro';---<Layout title="About"> <h1>About</h1> <p>Welcome to the about page.</p></Layout>Named slots
Section titled “Named slots”Astro supports named slots for injecting content into specific regions of a layout — similar to named children patterns in React (using render props or portals) but built into the language.
// React named-area pattern (render props)function Layout({ nav, main }: { nav: React.ReactNode; main: React.ReactNode;}) { return ( <div> <aside>{nav}</aside> <main>{main}</main> </div> );}
// Usage<Layout nav={<NavLinks />} main={<ArticleContent />}/>---// src/layouts/TwoColumn.astro---<div> <aside> <slot name="sidebar" /> <!-- named slot --> </aside> <main> <slot /> <!-- default slot --> </main></div>
<!-- Usage in a page -->---import TwoColumn from '../layouts/TwoColumn.astro';---<TwoColumn> <nav slot="sidebar"> <a href="/a">Link A</a> </nav> <article>Main content here</article></TwoColumn>Project file tree
Section titled “Project file tree”flowchart TD src["src/"] --> layouts["layouts/"] src --> pages["pages/"] layouts --> layout["Layout.astro (base HTML shell: head, nav, footer)"] layouts --> blogLayout["BlogLayout.astro (extends Layout, adds sidebar)"] pages --> index["index.astro imports Layout from '../layouts/Layout.astro'"] pages --> about["about.astro imports Layout from '../layouts/Layout.astro'"] pages --> blog["blog/"] blog --> slug["[slug].astro imports BlogLayout from '../../layouts/BlogLayout.astro'"]