Skip to main content
A layout file defines UI that is shared across multiple routes. Layouts persist across navigations and do not re-render when their child routes change.
app/dashboard/layout.js
export default function DashboardLayout({ children }) {
  return <section>{children}</section>
}

Root layout

The app directory must include a root layout.js. The root layout defines the <html> and <body> tags and is applied to every route.
app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
Do not add <head> tags like <title> or <meta> directly in the root layout. Use the Metadata API instead, which handles streaming and deduplication automatically.

Props

children
React.ReactNode
required
The nested layout or page rendered by this layout segment. May also include other special files like loading.js or error.js when applicable.
params
Promise<object>
A promise that resolves to the dynamic route parameters from the root segment down to the current layout.
app/dashboard/[team]/layout.js
export default async function Layout({ children, params }) {
  const { team } = await params
  return (
    <section>
      <h1>Team: {team}</h1>
      {children}
    </section>
  )
}
RouteURLparams
app/dashboard/[team]/layout.js/dashboard/1Promise<{ team: '1' }>
app/shop/[tag]/[item]/layout.js/shop/1/2Promise<{ tag: '1', item: '2' }>
app/blog/[...slug]/layout.js/blog/1/2Promise<{ slug: ['1', '2'] }>
Use async/await or React’s use() to read the value.

TypeScript helper

Use the global LayoutProps helper to get strongly typed props, including params and named parallel route slots:
app/dashboard/layout.tsx
export default function Layout(props: LayoutProps<'/dashboard'>) {
  return (
    <section>
      {props.children}
    </section>
  )
}
Types are generated during next dev, next build, or next typegen. LayoutProps is globally available and does not need to be imported.

Root layout requirements

  • Must define <html> and <body> tags
  • Must not add <head> tags directly — use the Metadata API instead
  • Can have multiple root layouts using route groups (e.g., app/(shop)/layout.js and app/(marketing)/layout.js)
  • Navigating between different root layouts causes a full page load (not client-side navigation)

Caveats

Request object

Layouts do not have direct access to the incoming request. Use the headers() and cookies() APIs from next/headers instead.
app/shop/layout.js
import { cookies } from 'next/headers'

export default async function Layout({ children }) {
  const cookieStore = await cookies()
  const theme = cookieStore.get('theme')
  return '...'
}

Query params

Layouts do not re-render on navigation, so searchParams in a layout would become stale. To access current query params, use the searchParams prop in a page, or use useSearchParams() in a Client Component.

Pathname

Layouts do not re-render on navigation. To access the current pathname, use usePathname() in a Client Component.

Fetching data

Layouts cannot pass fetched data directly to child pages. However, you can fetch the same data in both the layout and the page. Next.js automatically deduplicates fetch calls, so performance is not affected.

Examples

Exporting metadata

app/layout.js
export const metadata = {
  title: 'My App',
}

export default function Layout({ children }) {
  return '...'
}

Displaying content based on params

app/dashboard/[team]/layout.js
export default async function DashboardLayout({ children, params }) {
  const { team } = await params

  return (
    <section>
      <header>
        <h1>Welcome to {team}'s Dashboard</h1>
      </header>
      <main>{children}</main>
    </section>
  )
}

Reading params in a Client Component

Use React’s use() function to read the params promise in a Client Component:
app/page.js
'use client'

import { use } from 'react'

export default function Page({ params }) {
  const { slug } = use(params)
}

Accessing child route segment

Use useSelectedLayoutSegment() or useSelectedLayoutSegments() in a Client Component to read the active route segment below the layout:
app/ui/nav-link.js
'use client'

import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'

export default function NavLink({ slug, children }) {
  const segment = useSelectedLayoutSegment()
  const isActive = slug === segment

  return (
    <Link href={`/blog/${slug}`} style={{ fontWeight: isActive ? 'bold' : 'normal' }}>
      {children}
    </Link>
  )
}
app/ui/nav-links.js
'use client'

import { usePathname } from 'next/navigation'
import Link from 'next/link'

export function NavLinks() {
  const pathname = usePathname()

  return (
    <nav>
      <Link className={pathname === '/' ? 'active' : ''} href="/">Home</Link>
      <Link className={pathname === '/about' ? 'active' : ''} href="/about">About</Link>
    </nav>
  )
}

Version history

VersionChanges
v15.0.0-RCparams is now a promise
v13.0.0layout introduced

Build docs developers (and LLMs) love