404 Not Found

404 Not Found


nginx

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


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.tsx page, 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.

TEXT
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

100%
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:

TEXT
Diagram: layout persists across navigation (keeps state), template remounts (fresh instance).
TSX
// ============================================
// 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:

TEXT
Interactive component with state: sidebarState.
TSX
// 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>
  );
}
TSX
// 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:

TEXT
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

100%
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:

TEXT
Diagram of nested layout hierarchy from root → parent → child layouts.
TSX
// ============================================
// 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:

TEXT
RootLayout renders its UI.

Output:

TEXT
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.

100%
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:

TEXT
Diagram of route structure: file system paths map to URL paths.
TSX
// ============================================
// 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:

TEXT
Renders: TaskFlow | Collaborate and deliver
TSX
// 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:

TEXT
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

100%
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:

TEXT
Diagram: layout persists across navigation (keeps state), template remounts (fresh instance).
TSX
// ============================================
// 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:

TEXT
Interactive component with state: count.
TSX
// ============================================
// 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>
  );
}
TSX
// 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:

TEXT
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:

TEXT
The component renders the described UI in the browser.
TSX
// ============================================
// 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:

TEXT
Renders the Template component UI.

Output:

TEXT
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:

TEXT
The page renders as described above, with the UI updating based on the described behavior.
TSX
// ============================================
// 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:

TEXT
Renders: Root layout with Inter font, global CSS classes, and SEO metadata template ("%s | TaskFlow").

Output:

TEXT
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

100%
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

TSX
// ============================================
// 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:

TEXT
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

Q Can I use 'use client' in layout.tsx?
A Yes. layout.tsx is a Server Component by default, but if you need to use useState, useEffect, or event handlers, you can add the 'use client' directive. Note: Once added, this layout and all its child components become Client Components.
Q How do Route Groups affect URLs?
A Not at all. The URL for (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.
Q Can a layout and a template coexist?
A Yes. If both layout.tsx and template.tsx are present in the same directory, the template will be wrapped inside the layout. When the user navigates, the layout remains, and the template is rebuilt.
Q Can the <html> tag in the root layout be modified?
A Yes, and you must write it manually. The root layout automatically replaces Next.js’s default HTML wrapper, so you are free to add attributes such as lang, dir, className, and so on. This is key to RTL support.
Q What is the maximum number of nested layout levels?
A There is no strict limit, but we recommend not exceeding 3–4 levels. Each layout level creates an additional wrapping div in the DOM, and too many nested levels can impact performance and maintainability.
Q How can data be shared across different Route Groups?
A Through the Context Provider in the root layout. By wrapping 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


📝 Exercises

  1. Basic Exercise (⭐): Create two Route Group layouts, (marketing)/layout.tsx and (app)/layout.tsx, in the project, and apply different styles to the marketing pages (Home, About) and the app pages (Dashboard, Settings), respectively.

  2. Advanced Exercise (⭐⭐): Place both layout.tsx and template.tsx in the same directory. Add useEffect(() => { 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.

  3. Challenge (⭐⭐⭐): Create a UserProvider (Context) in the root layout, and use useUser() in both (auth)/login/page.tsx and (dashboard)/page.tsx to read user data, verifying that the Context shares data across Route Groups.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏