The root layout handles global UI, but what about sections that need their own unique layouts? A blog might need a sidebar, a dashboard needs navigation tabs, and admin pages require different headers. Nested layouts let you create layout hierarchies where each level adds its own UI layer. Layouts wrap inside layouts, all the way from root to page, giving you incredible flexibility to build sophisticated applications with clear separation of concerns.
How Nested Layouts Work
When you create a layout in a subfolder, it wraps all pages in that folder and its children:
app/
layout.tsx β Root layout
blog/
layout.tsx β Blog layout
page.tsx β /blog
[slug]/
page.tsx β /blog/my-postWhen visiting a page, layouts nest from root to leaf:
URL: /blog/my-post
Rendering hierarchy:
<RootLayout>
<BlogLayout>
<BlogPostPage />
</BlogLayout>
</RootLayout>Basic Nested Layout Structure
Blog section has its own layout nested under root
Select a file or folder to see details
Key Concept: Composition
Each layout receives children which contains the next layer (either another layout or the page). This creates a composition pattern:
- Root layout wraps everything
- Section layouts wrap their subsections
- Pages are the innermost children
Creating Your First Nested Layout
Let's build a blog with a sidebar that only shows on blog pages:
Step 1: Root Layout (Already Exists)
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{/* Global header */}
<header className="bg-blue-600 text-white p-4">
<nav>Site Navigation</nav>
</header>
{/* Page content (may include nested layouts) */}
<main>{children}</main>
{/* Global footer */}
<footer className="bg-gray-800 text-white p-4">
Footer
</footer>
</body>
</html>
);
}Step 2: Create Blog Layout
import Link from 'next/link';
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="container mx-auto px-4 py-8">
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
{/* Sidebar - only shows on blog pages */}
<aside className="lg:col-span-1">
<div className="bg-white rounded-lg shadow p-6 sticky top-4">
<h3 className="font-bold text-lg mb-4">Categories</h3>
<nav className="space-y-2">
<Link
href="/blog/category/tech"
className="block text-blue-600 hover:underline"
>
Technology
</Link>
<Link
href="/blog/category/design"
className="block text-blue-600 hover:underline"
>
Design
</Link>
<Link
href="/blog/category/business"
className="block text-blue-600 hover:underline"
>
Business
</Link>
</nav>
<h3 className="font-bold text-lg mt-6 mb-4">Recent Posts</h3>
<div className="space-y-3 text-sm">
<Link
href="/blog/post-1"
className="block text-gray-700 hover:text-blue-600"
>
Understanding Layouts
</Link>
<Link
href="/blog/post-2"
className="block text-gray-700 hover:text-blue-600"
>
Server Components Guide
</Link>
<Link
href="/blog/post-3"
className="block text-gray-700 hover:text-blue-600"
>
Dynamic Routing Explained
</Link>
</div>
<h3 className="font-bold text-lg mt-6 mb-4">Subscribe</h3>
<input
type="email"
placeholder="Your email"
className="w-full px-3 py-2 border rounded mb-2"
/>
<button className="w-full bg-blue-600 text-white py-2 rounded">
Subscribe
</button>
</div>
</aside>
{/* Main content - blog pages render here */}
<main className="lg:col-span-3">
{children}
</main>
</div>
</div>
);
}Step 3: Create Blog Pages
export default function BlogPage() {
return (
<div>
<h1 className="text-4xl font-bold mb-6">Latest Blog Posts</h1>
<div className="space-y-6">
{/* Blog posts list */}
</div>
</div>
);
}export default function BlogPostPage({
params
}: {
params: { slug: string }
}) {
return (
<article>
<h1 className="text-4xl font-bold mb-4">
{params.slug}
</h1>
<div className="prose max-w-none">
{/* Post content */}
</div>
</article>
);
}β¨ What Happens
When you visit blog pages:
/blog- Has header, footer, AND sidebar/blog/my-post- Has header, footer, AND sidebar/about- Has header and footer, NO sidebar
Multi-Level Nesting
You can nest layouts as deeply as needed:
app/
layout.tsx β Level 1: Global
dashboard/
layout.tsx β Level 2: Dashboard
page.tsx
analytics/
layout.tsx β Level 3: Analytics tabs
page.tsx
revenue/
page.tsxComplex Multi-Level Nesting
Three levels of nested layouts
Select a file or folder to see details
Example: Dashboard with Sub-navigation
import Link from 'next/link';
// Level 2: Dashboard layout with sidebar
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex h-screen">
{/* Dashboard sidebar */}
<aside className="w-64 bg-gray-900 text-white p-6">
<h2 className="text-xl font-bold mb-6">Dashboard</h2>
<nav className="space-y-2">
<Link
href="/dashboard"
className="block px-4 py-2 rounded hover:bg-gray-800"
>
π Overview
</Link>
<Link
href="/dashboard/analytics"
className="block px-4 py-2 rounded hover:bg-gray-800"
>
π Analytics
</Link>
<Link
href="/dashboard/users"
className="block px-4 py-2 rounded hover:bg-gray-800"
>
π₯ Users
</Link>
<Link
href="/dashboard/settings"
className="block px-4 py-2 rounded hover:bg-gray-800"
>
βοΈ Settings
</Link>
</nav>
</aside>
{/* Main dashboard content */}
<main className="flex-1 overflow-auto">
{children}
</main>
</div>
);
}import Link from 'next/link';
// Level 3: Analytics layout with tabs
export default function AnalyticsLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="p-8">
<h1 className="text-3xl font-bold mb-6">Analytics</h1>
{/* Tab navigation */}
<nav className="border-b mb-8">
<div className="flex gap-6">
<Link
href="/dashboard/analytics"
className="pb-2 border-b-2 border-transparent hover:border-blue-600"
>
Overview
</Link>
<Link
href="/dashboard/analytics/revenue"
className="pb-2 border-b-2 border-transparent hover:border-blue-600"
>
Revenue
</Link>
<Link
href="/dashboard/analytics/users"
className="pb-2 border-b-2 border-transparent hover:border-blue-600"
>
Users
</Link>
<Link
href="/dashboard/analytics/traffic"
className="pb-2 border-b-2 border-transparent hover:border-blue-600"
>
Traffic
</Link>
</div>
</nav>
{/* Analytics content */}
{children}
</div>
);
}Now the rendering hierarchy for /dashboard/analytics/revenue is:
<RootLayout> β Header + Footer
<DashboardLayout> β Sidebar
<AnalyticsLayout> β Tabs
<RevenuePage />
</AnalyticsLayout>
</DashboardLayout>
</RootLayout>Metadata in Nested Layouts
Nested layouts can define their own metadata, which merges with or overrides parent metadata:
import { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | My App', // Template for all pages
default: 'My App',
},
description: 'My awesome application',
};import { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | Blog | My App', // Override template for blog
default: 'Blog | My App',
},
description: 'Read our latest blog posts',
openGraph: {
type: 'website',
siteName: 'My App Blog',
},
};Now blog pages get the blog-specific metadata:
/aboutβ "About Us | My App" (root template)/blogβ "Blog | My App" (blog default)/blog/my-postβ "My Post | Blog | My App" (blog template)
Layout State Persistence
A key benefit of nested layouts is that their state persists when navigating between sibling pages:
'use client';
import { useState } from 'react';
import Link from 'next/link';
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
// This state persists across blog page navigations!
const [sidebarOpen, setSidebarOpen] = useState(true);
return (
<div className="flex">
{/* Toggle button */}
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="fixed top-20 left-4 z-10 bg-blue-600 text-white p-2 rounded"
>
{sidebarOpen ? 'β' : 'β'}
</button>
{/* Sidebar */}
{sidebarOpen && (
<aside className="w-64 p-6">
<nav className="space-y-2">
<Link href="/blog/post-1">Post 1</Link>
<Link href="/blog/post-2">Post 2</Link>
<Link href="/blog/post-3">Post 3</Link>
</nav>
</aside>
)}
{/* Content */}
<main className="flex-1">
{children}
</main>
</div>
);
}When navigating from /blog/post-1 to /blog/post-2:
- β The layout doesn't re-render
- β
sidebarOpenstate persists - β Only the page content changes
- β Smooth, fast navigation
When State Resets
Layout state only persists when:
- Navigating between pages under the same layout
- The layout itself doesn't change
If you navigate to a different section (e.g., /blog to /about), the blog layout unmounts and state is lost.
Common Nested Layout Patterns
1. Marketing + Dashboard Structure
app/
layout.tsx β Root: Basic HTML structure
(marketing)/
layout.tsx β Marketing: Header + Footer
page.tsx β /
about/
page.tsx β /about
pricing/
page.tsx β /pricing
dashboard/
layout.tsx β Dashboard: Sidebar + Top bar
page.tsx β /dashboard
analytics/
page.tsx β /dashboard/analytics2. Docs with Sidebar + TOC
app/
layout.tsx β Root: Global nav
docs/
layout.tsx β Docs: Sidebar navigation
[section]/
layout.tsx β Section: Table of contents
[page]/
page.tsx3. E-commerce with Different Layouts
app/
layout.tsx β Root: Site header
(shop)/
layout.tsx β Shop: Cart widget
products/
page.tsx
cart/
page.tsx
checkout/
layout.tsx β Checkout: Minimal, focused
page.tsx4. Multi-Tenant Application
app/
layout.tsx β Root: Auth wrapper
[tenant]/
layout.tsx β Tenant: Branding
dashboard/
layout.tsx β Dashboard: Navigation
page.tsxPractical Example: Complete Blog Structure
Blog Layout with Sidebar
A complete blog layout that wraps all blog pages
Output Preview
Nested Layout Best Practices
1. Keep Layouts Focused
Each layout should have a single, clear purpose:
// β
Good: Clear purpose
// app/blog/layout.tsx - Adds blog sidebar
// app/dashboard/layout.tsx - Adds dashboard nav
// β Bad: Mixed responsibilities
// app/section/layout.tsx - Sidebar + tabs + modals + forms2. Organize by Feature, Not Type
// β
Good: Organized by feature
app/
blog/
layout.tsx
[slug]/
page.tsx
dashboard/
layout.tsx
analytics/
page.tsx
// β Bad: Organized by type
app/
layouts/
blog.tsx
dashboard.tsx
pages/
blog/
dashboard/3. Extract Shared Components
// Extract reusable UI into components
// components/Sidebar.tsx
export function Sidebar({ links }) {
return <aside>{/* sidebar UI */}</aside>;
}
// app/blog/layout.tsx
import { Sidebar } from '@/components/Sidebar';
export default function BlogLayout({ children }) {
return (
<div className="flex">
<Sidebar links={blogLinks} />
<main>{children}</main>
</div>
);
}4. Consider Mobile First
export default function Layout({ children }) {
return (
<div className="flex flex-col lg:flex-row">
{/* Sidebar: Full width on mobile, fixed width on desktop */}
<aside className="w-full lg:w-64">
Sidebar
</aside>
{/* Content: Adapts to available space */}
<main className="flex-1">
{children}
</main>
</div>
);
}5. Use Descriptive Names
- β
app/dashboard/layout.tsx- Clear it's for dashboard - β
app/(marketing)/layout.tsx- Clear it's for marketing - β
app/layout2.tsx- Unclear purpose - β
app/section/layout.tsx- Too generic
Debugging Nested Layouts
Visualizing the Layout Tree
Add debug borders to see layout boundaries:
// Root layout
export default function RootLayout({ children }) {
return (
<html>
<body className="border-4 border-red-500">
{children}
</body>
</html>
);
}
// Blog layout
export default function BlogLayout({ children }) {
return (
<div className="border-4 border-blue-500">
{children}
</div>
);
}
// Page
export default function Page() {
return (
<div className="border-4 border-green-500">
Content
</div>
);
}Checking Which Layouts Render
export default function Layout({ children }) {
console.log('π BlogLayout rendered');
return <div>{children}</div>;
}
// Check console to see which layouts render on navigationKey Takeaways
- Layouts nest inside layouts - from root to page
- Each layout receives children - the next layer down
- Rendering is hierarchical - RootLayout(SectionLayout(Page))
- State persists across sibling pages - layouts don't re-render
- Metadata merges/overrides - child overrides parent
- Can nest indefinitely - as many levels as needed
- Perfect for section-specific UI - sidebars, tabs, navigation
- Keep layouts focused - one clear purpose each
What's Next?
You've mastered nested layoutsβa powerful tool for building complex applications! But there's a subtle variant you should know about: templates. While layouts persist across navigation, templates re-render every time.
In the next lesson, we'll explore template.tsx files, understand when to use them instead of layouts, and learn about the important differences in behavior. Templates are less commonly used, but essential for specific scenarios like animations or resetting state.
ποΈ Layout Architecture Matters
How you structure your layouts significantly impacts your app's maintainability. Take time to plan your layout hierarchyβit's much easier to get it right upfront than to refactor later!