Layouts & Templates
A layout system is like the floors of a building—each floor (layout) has shared hallways and facilities, while each room (page) can have different decor, and the state of the space’s users is preserved as they move between floors.
1. What You'll Learn
- Persistence Behavior and Configuration Methods for Nested Layouts
- The Impact of Logical Grouping and URLs in Route Groups
(group) - Key Differences Between Layouts and Templates and When to Use Each
- Essential configurations for the root layout:
<html>,<body>, font, metadata - Three Models for Data Sharing Between Layouts
2. A True Story of a Full-Stack Developer
(1) Pain Point: Having to repeatedly write the navigation bar and sidebar for every page
While developing the TaskFlow admin panel, Alice encountered an issue with duplicate layout code:
"Our team has five developers, each working on different pages. Everyone has to manually import the navigation bar and sidebar into their own page.tsx files. Last week, Charlie forgot to add the sidebar to the newly created
settings/page.tsxpage, so when a user clicked on the settings page, the menu suddenly disappeared, and they thought the navigation was broken."
Code Duplication:
| Issue | Impact | Number of Pages Affected |
|---|---|---|
| Duplicate imports in the navigation bar | Must be manually included on every page | 15 pages |
| Sidebar State Is Not Persistent | Selected state in the sidebar is lost after navigating | All Pages |
| Navigation displayed on the login/registration page | Should not be displayed; requires additional conditional checks | 3 pages |
| Complex user data transmission | User data must be fetched on every page | 12 pages |
(2) Solutions for the Next.js Layout System
Use nested layouts and Route Groups to isolate layouts; define them once for global application.
src/app/
├── layout.tsx # Root Layout(html/body/Font)
├── page.tsx # Home
├── (auth)/
│ ├── layout.tsx # Log In/Registration Layout(No sidebar)
│ ├── login/page.tsx
│ └── register/page.tsx
└── (dashboard)/
├── layout.tsx # Admin Panel Layout(Navigation+Sidebar)
├── page.tsx # Dashboard
├── projects/page.tsx # Project List
└── settings/page.tsx # Settings Page
(3) Revenue
| Dimension | Before (manual import) | After (layout system) |
|---|---|---|
| Navigation Bar References | 15 lines of import across 15 pages |
1 layout.tsx file |
| Sidebar Selection Status | Lost | Persistent |
| Login/Registration Layout | Conditional Checks | Natural Isolation via Route Groups |
| User Data Retrieval | 12 fetches | 1 shared layout |
3. Principles of Nested Layouts
(1) Layout Persistence Behavior
graph TB
subgraph "Page Navigation"
A[root layout] --> B[dashboard layout]
B --> C[page Dashboard]
B --> D[page Project List]
B --> E[page Settings]
end
subgraph "Behavior During Navigation"
F[layout Keep Mounted<br/>No loss of state]
G[page Unmount/Remount<br/>Content Replacement]
end
style F fill:#d4edda
style G fill:#f8d7da
| Behavior | layout | page |
|---|---|---|
| Remount during navigation | ❌ Do not remount | ✅ Remount |
| React State Preservation | ✅ Preserved | ❌ Reset |
| useEffect re-runs | ❌ Does not run | ✅ Runs |
| Retrieve Data | ❌ Do not retrieve | ✅ Retrieve |
▶ Example: Layout Persistence Demo
Output:
Diagram: layout persists across navigation (keeps state), template remounts (fresh instance).
// ============================================
// Persisting the Demo Layout vs Page Remounted
// ============================================
// src/app/(dashboard)/layout.tsx — Sidebar Layout
'use client';
import { useState } from "react";
import Link from "next/link";
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const [sidebarState, setSidebarState] = useState("collapsed");
return (
<div className="flex h-screen">
{/* Sidebar — Keep expanded while navigating/Collapsed state */}
<aside className={`bg-gray-800 text-white ${sidebarState === "collapsed" ? "w-16" : "w-64"} transition-all`}>
<button
onClick={() => setSidebarState(s =>
s === "collapsed" ? "expanded" : "collapsed"
)}
className="p-4 hover:bg-gray-700 w-full text-left"
>
{sidebarState === "collapsed" ? "→" : "← Collapse"}
</button>
<nav className="mt-4">
<Link href="/dashboard" className="block p-3 hover:bg-gray-700">Dashboard</Link>
<Link href="/dashboard/projects" className="block p-3 hover:bg-gray-700">Projects</Link>
<Link href="/dashboard/settings" className="block p-3 hover:bg-gray-700">Settings</Link>
</nav>
<div className="mt-4 p-3 text-sm text-gray-400">
Sidebar state persists across navigation
</div>
</aside>
<main className="flex-1 p-8 overflow-auto">
{children}
</main>
</div>
);
}
Output:
Interactive component with state: sidebarState.
// src/app/(dashboard)/dashboard/page.tsx
export default function DashboardPage() {
return (
<div>
<h1 className="text-2xl font-bold">Dashboard</h1>
<p className="text-gray-600">Welcome to your dashboard.</p>
</div>
);
}
// src/app/(dashboard)/dashboard/projects/page.tsx
export default function ProjectsPage() {
return (
<div>
<h1 className="text-2xl font-bold">Projects</h1>
<p className="text-gray-600">Your project list goes here.</p>
</div>
);
}
Output:
1. Visit /dashboard,Expand the sidebar,Show Dashboard Content
2. Click "→ Collapse",The sidebar collapses to 64px
3. Click "Projects"Link,Navigate to /dashboard/projects
4. Keep the sidebar collapsed(Not reset to expanded)✅
5. Page content starts at Dashboard become Projects ✅
(2) Hierarchy of Nested Layouts
graph TB
A[root layout] --> B[app/layout.tsx]
B --> C[(dashboard) layout]
C --> D[app/(dashboard)/layout.tsx]
D --> E[products layout]
E --> F[app/(dashboard)/products/layout.tsx]
F --> G[page]
G --> H[app/(dashboard)/products/page.tsx]
style B fill:#cce5ff
style D fill:#d4edda
style F fill:#f8d7da
| Layout Level | Scope | Shared Content |
|---|---|---|
| Root Layout | All Pages | <html>, <body>, Global Fonts, Global Styles |
| Group Layout | Pages within a group | Sidebar, navigation bar, user information |
| Nested Layout | Subdirectory Page | Subnavigation, Breadcrumbs, Local Filter Bar |
▶ Example: Three-level nested layout
Output:
Diagram of nested layout hierarchy from root → parent → child layouts.
// ============================================
// Three-level nested layout:Global → Backend → Product Management
// ============================================
// Level 1: src/app/layout.tsx — Root Layout
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className="bg-gray-50">
{children}
</body>
</html>
);
}
// Level 2: src/app/(dashboard)/layout.tsx — Backend Layout
export default function DashboardLayout({ children }) {
return (
<div className="flex">
<Sidebar />
<main className="flex-1">{children}</main>
</div>
);
}
// Level 3: src/app/(dashboard)/products/layout.tsx — Product Portfolio
export default function ProductsLayout({ children }) {
return (
<div>
<nav className="flex gap-4 border-b pb-2 mb-4">
<a href="/products" className="text-blue-600">All Products</a>
<a href="/products/add" className="text-blue-600">Add Product</a>
<a href="/products/categories" className="text-blue-600">Categories</a>
</nav>
{children}
</div>
);
}
Output:
RootLayout renders its UI.
Output:
Visit /dashboard/products:
→ Root Layout Rendering <html><body>
→ Backend Layout Rendering <Sidebar> + <main>
→ Product Layout Rendering Product Sub-Navigation + Page Content
All three levels of nesting are in effect
4. Route Groups (group)
(1) What Are Route Groups?
(group) Directories do not generate path segments in URLs; they are used solely for logical grouping.
graph LR
subgraph "File Structure"
A[app] --> B[(auth)]
A --> C[(dashboard)]
B --> D[login/page.tsx]
B --> E[register/page.tsx]
C --> F[page.tsx]
C --> G[settings/page.tsx]
end
subgraph "Corresponding URL"
H[/login]
I[/register]
J[/dashboard]
K[/dashboard/settings]
end
style B fill:#f8d7da
style C fill:#d4edda
| Directory | URL Path | Description |
|---|---|---|
app/(auth)/login/page.tsx |
/login |
(auth) No path segments generated |
app/(auth)/register/page.tsx |
/register |
(auth) No path segments generated |
app/(dashboard)/page.tsx |
/dashboard |
(dashboard) No path segments generated |
app/(dashboard)/settings/page.tsx |
/dashboard/settings |
Subpath is normal |
▶ Example: Using Route Groups to Implement Different Layouts
Output:
Diagram of route structure: file system paths map to URL paths.
// ============================================
// Usage Route Groups Separate the layouts of the login page and the admin dashboard
// ============================================
// src/app/(auth)/layout.tsx — Log In/Registration Layout(No Navigation、No sidebar)
export default function AuthLayout({ children }) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
<div className="w-full max-w-md">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-900">TaskFlow</h1>
<p className="text-gray-500">Collaborate and deliver</p>
</div>
<div className="bg-white p-8 rounded-xl shadow-sm">
{children}
</div>
</div>
</div>
);
}
// src/app/(auth)/login/page.tsx
export default function LoginPage() {
return (
<form className="space-y-4">
<h2 className="text-xl font-bold text-center">Sign In</h2>
<input
type="email"
placeholder="Email"
className="w-full p-3 border rounded-lg"
/>
<input
type="password"
placeholder="Password"
className="w-full p-3 border rounded-lg"
/>
<button
type="submit"
className="w-full p-3 bg-blue-600 text-white rounded-lg"
>
Sign In
</button>
</form>
);
}
Output:
Renders: TaskFlow | Collaborate and deliver
// src/app/(dashboard)/layout.tsx — Admin Panel Layout(Sidebar + Top Navigation)
export default function DashboardLayout({ children }) {
return (
<div className="flex h-screen">
<aside className="w-64 bg-gray-900 text-white">
<div className="p-4 text-xl font-bold">TaskFlow</div>
<nav className="mt-4">
<a href="/dashboard" className="block p-3 hover:bg-gray-800">
Dashboard
</a>
<a href="/dashboard/projects" className="block p-3 hover:bg-gray-800">
Projects
</a>
<a href="/dashboard/settings" className="block p-3 hover:bg-gray-800">
Settings
</a>
</nav>
</aside>
<div className="flex-1 flex flex-col">
<header className="bg-white shadow-sm p-4">
<input
type="search"
placeholder="Search..."
className="w-64 p-2 border rounded"
/>
</header>
<main className="flex-1 p-8 overflow-auto">{children}</main>
</div>
</div>
);
}
Output:
Visit /login See:
- Centered Form Design(No navigation bar、No sidebar)
- Blue gradient background
- TaskFlow Brand Logo + Sign In Form
Visit /dashboard See:
- Dark sidebar on the left(Dashboard / Projects / Settings)
- White search bar at the top
- Right-hand content area
5. Layout vs Template
(1) Key Differences
graph LR
A[Navigate to a new page] --> B{layout Or template?}
B -->|layout| C[Keep Mounted<br/>Persistent State]
B -->|template| D[Uninstall and Reinstall<br/>Remount]
C --> E[Child Component Updates]
D --> F[Child component + All wrapper containers have been rebuilt]
style C fill:#d4edda
style D fill:#f8d7da
| Characteristics | layout.tsx |
template.tsx |
|---|---|---|
| Remount during navigation | ❌ Keep mounted | ✅ Unmount + Remount |
| React State Preservation | ✅ Preserved | ❌ Reset |
| useEffect re-runs | ❌ Does not run | ✅ Runs |
| Page Transition Animations | Not suitable | ✅ Suitable |
| Refresh Data | ❌ Do not refresh | ✅ Refresh on every navigation |
| Performance | Better | Slightly worse (rebuilding overhead) |
▶ Example: Comparison of Layout vs. Template Behavior
Output:
Diagram: layout persists across navigation (keeps state), template remounts (fresh instance).
// ============================================
// layout vs template Behavior Comparison Demonstration
// ============================================
// src/app/(dashboard)/layout.tsx — Usage layout(Keep the following in mind while navigating)
'use client';
import { useEffect, useState } from "react";
import Link from "next/link";
export default function DashboardLayout({ children }) {
const [count, setCount] = useState(0);
const [mountTime] = useState(new Date().toLocaleTimeString());
useEffect(() => {
console.log("Layout mounted at:", new Date().toLocaleTimeString());
}, []);
return (
<div className="border-2 border-blue-500 p-4 rounded m-4">
<div className="text-sm text-blue-600 mb-2">
[LAYOUT] Mounted at: {mountTime} | Count: {count}
<button onClick={() => setCount(c => c + 1)} className="ml-2 px-2 bg-blue-100 rounded">
+1
</button>
</div>
<nav className="flex gap-4 mb-4">
<Link href="/dashboard/page-a" className="text-blue-600">Page A</Link>
<Link href="/dashboard/page-b" className="text-blue-600">Page B</Link>
</nav>
{children}
</div>
);
}
Output:
Interactive component with state: count.
// ============================================
// template.tsx — Rebuild During Navigation
// Documents:src/app/(dashboard)/template.tsx
// ============================================
'use client';
import { useEffect, useState } from "react";
import Link from "next/link";
export default function DashboardTemplate({ children }) {
const [count, setCount] = useState(0);
const [mountTime] = useState(new Date().toLocaleTimeString());
useEffect(() => {
console.log("Template mounted at:", new Date().toLocaleTimeString());
}, []);
return (
<div className="border-2 border-red-500 p-4 rounded m-4">
<div className="text-sm text-red-600 mb-2">
[TEMPLATE] Mounted at: {mountTime} | Count: {count}
<button onClick={() => setCount(c => c + 1)} className="ml-2 px-2 bg-red-100 rounded">
+1
</button>
</div>
<nav className="flex gap-4 mb-4">
<Link href="/dashboard/page-a" className="text-red-600">Page A</Link>
<Link href="/dashboard/page-b" className="text-red-600">Page B</Link>
</nav>
{children}
</div>
);
}
// src/app/(dashboard)/page-a.tsx
export default function PageA() {
return <div className="text-lg">Page A Content</div>;
}
// src/app/(dashboard)/page-b.tsx
export default function PageB() {
return <div className="text-lg">Page B Content</div>;
}
Output:
1. Visit /dashboard/page-a:
[LAYOUT] Mounted at: 10:30:00 | Count: 0
[TEMPLATE] Mounted at: 10:30:00 | Count: 0
Page A Content
2. Click the Count +1 button (two containers' Count increment independently)
3. Click "Page B" Link:
[LAYOUT] Mounted at: 10:30:00 | Count: 1 ← layout Maintain,State Preservation
[TEMPLATE] Mounted at: 10:30:05 | Count: 0 ← template Rebuild,Reset Status
Page B Content
Conclusion:layout Maintain Mounts and Status,template Every time the navigation is rebuilt
(2) When to Use a Template
| Scenario | Recommendation | Reason |
|---|---|---|
| Page Transition Animations | template | framer-motion Exit/Entry Animations |
| Data must be refreshed every time the navigation occurs | template | useEffect re-runs |
| Navigation Analysis Tracking | template | Trigger the tracking code on every navigation |
| Navigation Bar/Sidebar | layout | Keep selected and expanded/collapsed |
| Shopping Cart/Player | layout | Persistent UI State |
▶ Example: Implementing page transition animations using templates
Output:
The component renders the described UI in the browser.
// ============================================
// template + framer-motion Implementing Page Transition Animations
// ============================================
'use client';
import { motion } from "framer-motion";
export default function Template({ children }: { children: React.ReactNode }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
{children}
</motion.div>
);
}
Output:
Renders the Template component UI.
Output:
Every time you navigate to a page:
1. The old page fades out upward(opacity: 1→0, y: 0→-20)
2. The new page fades in from the bottom(opacity: 0→1, y: 20→0)
3. Animation Length 300ms
4. layout Keep, Do Not Rebuild,Animations apply only to the content area
6. Essential Configuration for Root Layouts
(1) Root Layout Responsibilities
The root layout is the only required layout file; it defines the framework for all pages:
| Configuration Option | Code | Description |
|---|---|---|
<html> Tag |
<html lang="en"> |
Language attributes that affect SEO |
<body> Tag |
<body className="..."> |
Global CSS Class |
| Font Loading | next/font/google |
Performance-Optimized Fonts |
| Metadata | export const metadata |
Global SEO Metadata |
| Global Styles | import './globals.css' |
Tailwind Directives |
▶ Example: Complete Root Layout
Output:
The page renders as described above, with the UI updating based on the described behavior.
// ============================================
// Root Layout — Complete Configuration
// Documents:src/app/layout.tsx
// ============================================
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
// Google Font Optimization(Automatic preload + CSS size-adjust)
const inter = Inter({
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
});
// Global SEO metadata
export const metadata: Metadata = {
title: {
template: "%s | TaskFlow",
default: "TaskFlow - Project Management",
},
description: "A collaborative project management platform",
openGraph: {
title: "TaskFlow",
description: "Collaborate and deliver projects faster",
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={inter.variable}>
<body className="antialiased bg-gray-50 text-gray-900 min-h-screen">
{children}
</body>
</html>
);
}
Output:
Renders: Root layout with Inter font, global CSS classes, and SEO metadata template ("%s | TaskFlow").
Output:
All pages are automatically generated:
- Inter Font (Performance Optimization, zero CLS)
- Global CSS classes (antialiased, bg-gray-50, text-gray-900)
- SEO metadata(title Template "Page | TaskFlow")
- Open Graph Tags(Social Media Sharing Cards)
7. Designing Data Sharing Models
(1) Three Sharing Models
graph TB
subgraph "Data Sharing Models"
A[1. Props Pass<br/>layout → page]
B[2. Context Provider<br/>Global State]
C[3. Parallel Data Retrieval<br/>layout + page Obtain individually]
end
style A fill:#d4edda
style B fill:#cce5ff
style C fill:#f8d7da
| Mode | Applicable Scenarios | Advantages | Disadvantages |
|---|---|---|---|
| Prop Passing | layout retrieves data and passes it to the page | type-safe | passes down only one level |
| Context Provider | User information, themes | Globally available | Required for Client Component |
| Parallel Data Retrieval | Independent data for layout and page | Decoupling, concurrency | Cannot be shared directly |
8. Complete Example: TaskFlow Complete Layout System
// ============================================
// Comprehensive Example:TaskFlow Comprehensive Layout System
// Root Layout + Auth Layout + Dashboard Layout + Product Portfolio
// ============================================
// src/app/layout.tsx — Root Layout
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"], display: "swap" });
export const metadata: Metadata = {
title: { template: "%s | TaskFlow", default: "TaskFlow" },
description: "Project management platform",
};
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.className}>
<body className="bg-gray-50 antialiased">{children}</body>
</html>
);
}
// src/app/(auth)/layout.tsx — Auth Layout
export default function AuthLayout({ children }) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-blue-600 to-indigo-700">
<div className="w-full max-w-md">
<div className="text-center text-white mb-8">
<h1 className="text-4xl font-bold">TaskFlow</h1>
<p className="text-blue-200 mt-2">Collaborate and deliver</p>
</div>
<div className="bg-white rounded-xl shadow-2xl p-8">
{children}
</div>
</div>
</div>
);
}
// src/app/(dashboard)/layout.tsx — Dashboard Layout
'use client';
import { createContext, useContext, useState } from "react";
import Link from "next/link";
const DashboardContext = createContext(null);
export function useDashboard() { return useContext(DashboardContext); }
export default function DashboardLayout({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(true);
const user = { name: "Alice", role: "Admin", avatar: "/avatar.png" };
return (
<DashboardContext.Provider value={{ user, sidebarOpen, setSidebarOpen }}>
<div className="flex h-screen">
<aside className={`bg-gray-900 text-white ${sidebarOpen ? "w-64" : "w-16"} transition-all duration-300`}>
<div className="p-4 flex items-center gap-3">
<div className="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-sm">
{user.name[0]}
</div>
{sidebarOpen && <span className="font-bold">TaskFlow</span>}
</div>
<nav className="mt-4">
<Link href="/dashboard" className="flex items-center gap-3 p-3 hover:bg-gray-800">
<span>📊</span>
{sidebarOpen && <span>Dashboard</span>}
</Link>
<Link href="/dashboard/projects" className="flex items-center gap-3 p-3 hover:bg-gray-800">
<span>📁</span>
{sidebarOpen && <span>Projects</span>}
</Link>
<Link href="/dashboard/team" className="flex items-center gap-3 p-3 hover:bg-gray-800">
<span>👥</span>
{sidebarOpen && <span>Team</span>}
</Link>
<Link href="/dashboard/settings" className="flex items-center gap-3 p-3 hover:bg-gray-800">
<span>⚙️</span>
{sidebarOpen && <span>Settings</span>}
</Link>
</nav>
<button
onClick={() => setSidebarOpen(!sidebarOpen)}
className="absolute bottom-4 left-4 text-gray-400 hover:text-white"
>
{sidebarOpen ? "◀" : "▶"}
</button>
</aside>
<div className="flex-1 flex flex-col">
<header className="bg-white shadow-sm px-8 py-4 flex items-center justify-between">
<h2 className="text-lg font-semibold text-gray-700">
Welcome, {user.name}
</h2>
<div className="flex items-center gap-4">
<button className="text-gray-500">🔔</button>
<div className="w-8 h-8 bg-gray-300 rounded-full" />
</div>
</header>
<main className="flex-1 p-8 overflow-auto">{children}</main>
</div>
</div>
</DashboardContext.Provider>
);
}
// src/app/(dashboard)/dashboard/page.tsx — Dashboard Home Page
'use client';
import { useDashboard } from "../layout";
export default function DashboardHomePage() {
const { user } = useDashboard();
return (
<div>
<h1 className="text-3xl font-bold">Dashboard Overview</h1>
<p className="text-gray-500 mt-2">
Welcome back, {user.name}! Here is your project summary.
</p>
<div className="grid grid-cols-3 gap-6 mt-8">
<div className="bg-white p-6 rounded-xl shadow-sm">
<div className="text-sm text-gray-500">Active Projects</div>
<div className="text-3xl font-bold mt-2">12</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-sm">
<div className="text-sm text-gray-500">Pending Tasks</div>
<div className="text-3xl font-bold mt-2">48</div>
</div>
<div className="bg-white p-6 rounded-xl shadow-sm">
<div className="text-sm text-gray-500">Team Members</div>
<div className="text-3xl font-bold mt-2">8</div>
</div>
</div>
</div>
);
}
Expected Output:
Visit /login:
- Dark blue gradient full-screen background
- Centered White Form Card
- TaskFlow Logo + Login Form
Visit /dashboard:
- Dark sidebar on the left(Foldable,Maintain Status While Navigating)
- Top Navigation Bar(Show Welcome, Alice + Notification Icon)
- Right-hand content area(Dashboard Overview + 3 A Statistics Card)
- User data is transmitted via Context Share
❓ FAQ
(auth)/login/page.tsx remains /login; (auth) only affects the layout structure and does not generate URL path segments. This is the core purpose of Route Groups.<html> tag in the root layout be modified?lang, dir, className, and so on. This is key to RTL support.div in the DOM, and too many nested levels can impact performance and maintainability.UserProvider, ThemeProvider, and so on in the root layout, pages within all Route Groups can access them. This is the global data-sharing method recommended by Next.js.📖 Summary
- Nested layouts remain mounted during navigation, and React state is preserved
- Route Groups
(group)do not generate URL path segments; they are used solely for logical grouping and layout isolation. - Layout is best suited for persistent UIs, while template is best suited for animations and data refreshes
- The root layout serves as the framework for all pages and must include
<html>,<body>, and metadata - A Context Provider is the recommended way to share data between layouts.
- Most Common Use Case for Route Groups: Separating the layouts of the login page (no navigation) and the admin dashboard (with navigation)
- The layout uses the Server Component by default; add 'use client' when interaction is required
📝 Exercises
-
Basic Exercise (⭐): Create two Route Group layouts,
(marketing)/layout.tsxand(app)/layout.tsx, in the project, and apply different styles to the marketing pages (Home, About) and the app pages (Dashboard, Settings), respectively. -
Advanced Exercise (⭐⭐): Place both
layout.tsxandtemplate.tsxin the same directory. AdduseEffect(() => { console.log('mounted') }, [])to each file, then navigate through the pages and observe the console output to verify the differences in how they are mounted. -
Challenge (⭐⭐⭐): Create a UserProvider (Context) in the root layout, and use
useUser()in both(auth)/login/page.tsxand(dashboard)/page.tsxto read user data, verifying that the Context shares data across Route Groups.



