Most websites have UI that appears on multiple pages—navigation bars, footers, sidebars. Without layouts, you'd duplicate this code in every page, making maintenance a nightmare. Layouts solve this by letting you create shared UI once and wrap multiple pages with it. Even better, layouts persist across navigation, meaning they don't re-render when users move between pages. This keeps state intact and improves performance. Let's master this fundamental Next.js concept!
What Are Layouts?
A layout is a UI component that wraps one or more pages. It's defined in a layout.tsx file and uses React's children prop to render page content inside it.
The Problem Without Layouts
Without layouts, you'd repeat code in every page:
export default function HomePage() {
return (
<>
<header>
<nav>{/* Navigation */}</nav>
</header>
<main>
{/* Homepage content */}
</main>
<footer>{/* Footer */}</footer>
</>
);
}export default function AboutPage() {
return (
<>
<header>
<nav>{/* Same navigation - duplicated! */}</nav>
</header>
<main>
{/* About content */}
</main>
<footer>{/* Same footer - duplicated! */}</footer>
</>
);
}Problems with this approach:
- Code duplication everywhere
- Hard to maintain - change one, change all
- Header/footer re-render on every navigation
- State doesn't persist (like open menus)
The Solution: Layouts
With layouts, you define shared UI once:
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<header>
<nav>{/* Navigation - defined once! */}</nav>
</header>
<main>{children}</main>
<footer>{/* Footer - defined once! */}</footer>
</>
);
}Now your pages are just the unique content:
export default function HomePage() {
return <div>{/* Just homepage content */}</div>;
}export default function AboutPage() {
return <div>{/* Just about content */}</div>;
}Benefits of Layouts
- No duplication: Write shared UI once
- Easy maintenance: Change in one place
- Persistent UI: Layouts don't re-render on navigation
- State preservation: Layout state persists between pages
Creating Your First Layout
Let's create a simple layout with a header and footer:
Step 1: Create layout.tsx
import Link from 'next/link';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{/* Header - shows on all pages */}
<header className="bg-blue-600 text-white">
<nav className="container mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<div className="text-xl font-bold">My App</div>
<div className="flex gap-6">
<Link href="/" className="hover:underline">
Home
</Link>
<Link href="/about" className="hover:underline">
About
</Link>
<Link href="/blog" className="hover:underline">
Blog
</Link>
<Link href="/contact" className="hover:underline">
Contact
</Link>
</div>
</div>
</nav>
</header>
{/* Page content renders here */}
<main className="min-h-screen">
{children}
</main>
{/* Footer - shows on all pages */}
<footer className="bg-gray-800 text-white py-8">
<div className="container mx-auto px-4 text-center">
<p>© 2024 My App. All rights reserved.</p>
</div>
</footer>
</body>
</html>
);
}Step 2: Create Pages
Pages automatically get wrapped by the layout:
export default function HomePage() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-4">Welcome Home</h1>
<p className="text-lg">This page is wrapped by the layout!</p>
</div>
);
}export default function AboutPage() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold mb-4">About Us</h1>
<p className="text-lg">This page also uses the same layout!</p>
</div>
);
}✨ Automatic Wrapping
You don't need to import or reference the layout. Next.js automatically wraps pages with layouts in their route segment. It just works!
How Layouts Work
The children Prop
Layouts receive a children prop that contains the page content:
export default function Layout({
children, // This is the page content
}: {
children: React.ReactNode;
}) {
return (
<div>
<header>Header</header>
{children} {/* Page renders here */}
<footer>Footer</footer>
</div>
);
}Layout Hierarchy
When you visit a page, Next.js:
- Finds all layouts in the route path
- Nests them from root to leaf
- Renders the page as the innermost child
URL: /blog/my-post
Rendering hierarchy:
app/layout.tsx
└─ app/blog/layout.tsx
└─ app/blog/[slug]/page.tsx
Result:
<RootLayout>
<BlogLayout>
<PostPage />
</BlogLayout>
</RootLayout>Layout Structure Example
See how layouts wrap pages at different levels
Select a file or folder to see details
Creating Section-Specific Layouts
You can create layouts for specific sections of your app:
Example: Blog Layout
app/
layout.tsx ← Root layout (header + footer)
blog/
layout.tsx ← Blog layout (sidebar)
page.tsx → /blog
[slug]/
page.tsx → /blog/my-postimport 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 hover:text-blue-600">
Understanding Next.js Layouts
</Link>
<Link href="/blog/post-2" className="block hover:text-blue-600">
Building with Server Components
</Link>
</div>
</div>
</aside>
{/* Main content - blog pages render here */}
<main className="lg:col-span-3">
{children}
</main>
</div>
</div>
);
}Now all blog pages have a sidebar, but other pages don't!
Example: Dashboard Layout
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex h-screen">
{/* Sidebar navigation */}
<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">
<a href="/dashboard" className="block px-4 py-2 rounded hover:bg-gray-800">
📊 Overview
</a>
<a href="/dashboard/analytics" className="block px-4 py-2 rounded hover:bg-gray-800">
📈 Analytics
</a>
<a href="/dashboard/users" className="block px-4 py-2 rounded hover:bg-gray-800">
👥 Users
</a>
<a href="/dashboard/settings" className="block px-4 py-2 rounded hover:bg-gray-800">
⚙️ Settings
</a>
</nav>
</aside>
{/* Main dashboard content */}
<main className="flex-1 overflow-auto bg-gray-50 p-8">
{children}
</main>
</div>
);
}Layout State Persists
One of the most powerful features of layouts is that their state persists across page navigations:
'use client';
import { useState } from 'react';
import Link from 'next/link';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// This state persists across page navigations!
const [menuOpen, setMenuOpen] = useState(false);
return (
<html lang="en">
<body>
<header className="bg-blue-600 text-white p-4">
<div className="flex items-center justify-between">
<div className="text-xl font-bold">My App</div>
{/* Mobile menu button */}
<button
onClick={() => setMenuOpen(!menuOpen)}
className="lg:hidden"
>
{menuOpen ? '✕' : '☰'}
</button>
{/* Navigation */}
<nav className={`${menuOpen ? 'block' : 'hidden'} lg:block`}>
<Link href="/" className="mx-4">Home</Link>
<Link href="/about" className="mx-4">About</Link>
<Link href="/blog" className="mx-4">Blog</Link>
</nav>
</div>
</header>
<main>{children}</main>
</body>
</html>
);
}When you navigate between pages, the menuOpen state persists! If the menu is open and you navigate to another page, it stays open.
Why This Matters
Persistent state enables:
- Better UX: UI state doesn't reset unexpectedly
- Performance: Layout doesn't re-render on navigation
- Smooth transitions: Sidebar, modals, etc. stay in place
- Complex UI: Maintain application-level state
Client vs Server Components in Layouts
Server Component Layouts (Default)
By default, layouts are Server Components:
// Server Component (default)
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
// Can fetch data
const user = await fetch('https://api.example.com/user')
.then(r => r.json());
return (
<html lang="en">
<body>
<header>
<nav>
{user ? `Welcome, ${user.name}` : 'Login'}
</nav>
</header>
<main>{children}</main>
</body>
</html>
);
}Client Component Layouts (When Needed)
Add "use client" when you need interactivity:
'use client';
import { useState } from 'react';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const [sidebarOpen, setSidebarOpen] = useState(true);
return (
<html lang="en">
<body>
<button onClick={() => setSidebarOpen(!sidebarOpen)}>
Toggle Sidebar
</button>
<div className="flex">
{sidebarOpen && <aside>Sidebar</aside>}
<main>{children}</main>
</div>
</body>
</html>
);
}Important: Choose Wisely
Making a layout a Client Component means all pages it wraps become Client Components too. Prefer:
- Server Components for layouts when possible
- Client Components only for interactive parts (move to separate components)
Best Practice: Separate Interactive Components
'use client';
import { useState } from 'react';
export function MobileMenu() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(!open)}>
{open ? '✕' : '☰'}
</button>
{open && (
<nav>
{/* Menu items */}
</nav>
)}
</>
);
}// Server Component (better!)
import { MobileMenu } from '@/components/MobileMenu';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<header>
<MobileMenu /> {/* Only this is a Client Component */}
</header>
<main>{children}</main>
</body>
</html>
);
}Common Layout Patterns
1. Marketing Site Layout
export default function MarketingLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<>
<header className="sticky top-0 bg-white shadow">
<nav>{/* Marketing navigation */}</nav>
</header>
<main>{children}</main>
<footer className="bg-gray-900 text-white">
{/* Footer with links, newsletter, etc. */}
</footer>
</>
);
}2. Application Layout (Sidebar)
export default function AppLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex h-screen">
<aside className="w-64 bg-gray-900">
{/* Sidebar navigation */}
</aside>
<div className="flex-1 flex flex-col">
<header className="h-16 border-b">
{/* Top bar */}
</header>
<main className="flex-1 overflow-auto p-8">
{children}
</main>
</div>
</div>
);
}3. Centered Content Layout
export default function CenteredLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="w-full max-w-md">
{children}
</div>
</div>
);
}4. Two-Column Layout
export default function TwoColumnLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="container mx-auto px-4 py-8">
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<aside className="lg:col-span-1">
{/* Sidebar content */}
</aside>
<main className="lg:col-span-2">
{children}
</main>
</div>
</div>
);
}Layout Best Practices
1. Keep Layouts Simple
Layouts should focus on structure and shared UI, not complex logic:
// ✅ Good: Simple, structural
export default function Layout({ children }) {
return (
<div>
<Header />
<main>{children}</main>
<Footer />
</div>
);
}Avoid putting too much logic in layouts:
// ❌ Avoid: Too much logic
export default function Layout({ children }) {
// Don't put complex business logic here
const complexCalculation = /* ... */;
const processedData = /* ... */;
return <div>{/* ... */}</div>;
}2. Use Metadata for SEO
import { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | My App', // Page titles will use this template
default: 'My App',
},
description: 'Welcome to My App',
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}3. Compose Layouts, Don't Duplicate
Extract shared components instead of duplicating:
// components/AppHeader.tsx
export function AppHeader() {
return <header>{/* Header UI */}</header>;
}
// Use in multiple layouts
import { AppHeader } from '@/components/AppHeader';
export default function Layout({ children }) {
return (
<div>
<AppHeader />
{children}
</div>
);
}4. Consider Mobile Responsiveness
export default function Layout({ children }) {
return (
<div className="flex flex-col lg:flex-row">
{/* Stack on mobile, side-by-side on desktop */}
<aside className="w-full lg:w-64">
Sidebar
</aside>
<main className="flex-1">
{children}
</main>
</div>
);
}Practice: Build a Layout
Complete Layout Example
A full layout with header, footer, and responsive design
Output Preview
🎯 Challenge
Create these layouts in your project:
- Root layout with header and footer
- Blog layout with sidebar
- Dashboard layout with navigation
- Test that state persists when navigating
Key Takeaways
- Layouts share UI across pages - no duplication needed
- Use layout.tsx files - automatically wrap pages
- Receive children prop - page content renders there
- State persists across navigation - layouts don't re-render
- Can nest layouts - root layout + section layouts
- Server Components by default - use "use client" when needed
- Extract interactive components - keep layouts as Server Components
- Keep layouts simple - focus on structure, not logic
What's Next?
You've learned the fundamentals of layouts! But there's a special layout that's required in every Next.js app—the root layout. In the next lesson, we'll dive deep into the root layout and learn how to configure global settings, manage the HTML and body tags, and set up app-wide configurations.
Understanding the root layout is crucial because it's the foundation of your entire application. Let's master it!
🎓 Practice Makes Perfect
Layouts are fundamental to Next.js. Take time to experiment with different layout patterns in your project. Try creating layouts for different sections and see how they compose together!