Next.js Layout System — Nested Layouts Guide
Advertisement
Next.js Layout System — Nested Layouts Guide
Layouts wrap pages with shared UI. Nested layouts enable sophisticated organization where different route sections have different persistent layouts.
- Next.js Layout System — Nested Layouts Guide
- Root Layout
- Nested Layouts
- Layout Inheritance
- Multiple Layouts
- Route Groups for Layout Organization
- Dynamic Layouts
- Metadata in Layouts
- Real-World: E-Commerce Layout
- Layout Persistence
- FAQ
Root Layout
Every app needs a root layout:
// app/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
description: 'Welcome to my app'
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<header>Navigation here</header>
<main>{children}</main>
<footer>Footer content</footer>
</body>
</html>
)
}
This wraps every page in your app.
Nested Layouts
Create layouts at any level to wrap route segments:
app/
├── layout.tsx → Root layout
├── page.tsx → /
├── blog/
│ ├── layout.tsx → /blog/* layout
│ ├── page.tsx → /blog
│ └── [slug]/
│ └── page.tsx → /blog/[slug]
├── admin/
│ ├── layout.tsx → /admin/* layout
│ └── page.tsx → /admin
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="grid grid-cols-4 gap-4">
<aside className="col-span-1">
<nav>
<a href="/blog">All Posts</a>
<a href="/blog/category/react">React</a>
<a href="/blog/category/nextjs">Next.js</a>
</nav>
</aside>
<main className="col-span-3">{children}</main>
</div>
)
}
Layout Inheritance
Child layouts inherit parent layout structure:
Root (header + footer) ─┐
├── Blog Layout (sidebar) ─┐
│ ├── Post Page
│ ├── Category Page
│
├── Admin Layout (auth) ─┐
│ ├── Dashboard
│ ├── Settings
Multiple Layouts
Create different layouts for different sections:
// app/marketing/layout.tsx (Marketing site layout)
export default function MarketingLayout({ children }) {
return (
<div>
<header className="bg-white border-b">
<nav>Marketing Nav</nav>
</header>
<main>{children}</main>
<footer>Marketing Footer</footer>
</div>
)
}
// app/app/layout.tsx (Application layout)
export default function AppLayout({ children }) {
return (
<div className="flex">
<aside className="w-64 bg-gray-900 text-white">
<nav>App Nav</nav>
</aside>
<div className="flex-1 flex flex-col">
<header className="border-b">App Header</header>
<main className="flex-1">{children}</main>
</div>
</div>
)
}
app/
├── layout.tsx → Root
├── page.tsx → Landing page
├── marketing/
│ ├── layout.tsx → Marketing layout
│ ├── page.tsx → /marketing
│ └── about/
│ └── page.tsx → /marketing/about
├── app/
│ ├── layout.tsx → App layout
│ ├── page.tsx → /app (dashboard)
│ └── settings/
│ └── page.tsx → /app/settings
Route Groups for Layout Organization
Use route groups to organize layouts without affecting URLs:
app/
├── layout.tsx → Root
├── (marketing)/
│ ├── layout.tsx → Marketing layout
│ ├── page.tsx → / (root)
│ ├── about/
│ │ └── page.tsx → /about
│ └── blog/
│ └── page.tsx → /blog
├── (admin)/
│ ├── layout.tsx → Admin layout
│ ├── page.tsx → / (but different layout!)
│ └── users/
│ └── page.tsx → /users
// app/(marketing)/layout.tsx
export default function MarketingLayout({ children }) {
return (
<div>
<nav>Marketing Nav</nav>
{children}
</div>
)
}
// app/(admin)/layout.tsx
export default function AdminLayout({ children }) {
return (
<div className="flex">
<sidebar>Admin Sidebar</sidebar>
<main>{children}</main>
</div>
)
}
Now / and /about have marketing layout, while /admin and /admin/users have admin layout.
Dynamic Layouts
Create layouts that change based on data:
// app/user/[id]/layout.tsx
async function getUser(id: string) {
return db.users.findUnique({ where: { id } })
}
export default async function UserLayout({
children,
params,
}: {
children: React.ReactNode
params: { id: string }
}) {
const user = await getUser(params.id)
return (
<div className="grid gap-4">
<header className="bg-blue-600 text-white p-4">
<h1>{user.name}'s Profile</h1>
</header>
<div className="grid grid-cols-4 gap-4">
<aside className="col-span-1">
<img src={user.avatar} alt={user.name} className="rounded" />
<p>{user.bio}</p>
</aside>
<main className="col-span-3">{children}</main>
</div>
</div>
)
}
Metadata in Layouts
Define metadata at layout level:
// app/blog/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Blog',
description: 'Read my latest blog posts',
}
export default function BlogLayout({ children }) {
return <div>{children}</div>
}
Child pages can override this metadata.
Real-World: E-Commerce Layout
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html>
<body>
<header>
<Logo />
<SearchBar />
<CartIcon />
</header>
<main>{children}</main>
<footer>
<Links />
<Newsletter />
</footer>
</body>
</html>
)
}
// app/shop/layout.tsx
export default function ShopLayout({ children }) {
return (
<div className="grid grid-cols-4 gap-4">
<aside className="col-span-1">
<CategoryFilter />
<PriceFilter />
</aside>
<main className="col-span-3">{children}</main>
</div>
)
}
// app/shop/page.tsx
export default function ShopPage() {
return <ProductGrid />
}
// app/shop/[id]/layout.tsx
export default function ProductLayout({ children }) {
return (
<div className="grid grid-cols-2 gap-4">
<div>{children}</div>
<aside>
<RelatedProducts />
<Reviews />
</aside>
</div>
)
}
Layout Persistence
Layouts persist across page navigation, maintaining state:
// app/dashboard/layout.tsx
'use client'
import { useState } from 'react'
export default function DashboardLayout({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(true)
return (
<div className="flex">
<aside className={sidebarOpen ? 'w-64' : 'w-16'}>
<Sidebar open={sidebarOpen} />
</aside>
<main className="flex-1">
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
Toggle Sidebar
</button>
{children}
</main>
</div>
)
}
As users navigate /dashboard/analytics to /dashboard/users, the sidebar state persists.
FAQ
Q: Can I have different layouts for different pages in the same route? A: Yes, use route groups to create separate nested layouts for different sections.
Q: How do I prevent a layout from persisting? A: Layouts persist by design. Use Client Components with state management if you need to clear state on navigation.
Q: Can layouts fetch data? A: Yes, layouts can be async Server Components and fetch data.
Master layouts for clean, organized application structure.
Advertisement