Layouts & Nested Layouts
Layouts & Nested Layouts in Next.js
Layouts are one of Next.js's most useful features. They let you define a UI shell — navigation, sidebar, footer — that wraps specific routes without re-rendering when the user navigates between those routes. Understanding the layout system unlocks clean, performant app structure.
What a Layout Does
A layout.tsx file wraps all pages at its level and below. The layout itself doesn't unmount when the user navigates to a child route — only the {children} content changes:
// src/app/layout.tsx — wraps EVERY page in the app
import type { Metadata } from "next";
export const metadata: Metadata = {
title: { template: "%s | AiTechWorlds", default: "AiTechWorlds" },
description: "Learn React, Next.js, Python, and AI.",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<TopNav />
{children}
<Footer />
</body>
</html>
);
}
Every page gets <TopNav> and <Footer> automatically. No need to include them in each page component.
Nested Layouts
Create a layout.tsx inside a folder to apply it only to routes in that folder:
src/app/
├── layout.tsx → Root layout: TopNav + Footer (all routes)
├── page.tsx → /
├── about/
│ └── page.tsx → /about (uses root layout only)
└── dashboard/
├── layout.tsx → Dashboard layout: Sidebar + main area
├── page.tsx → /dashboard
├── settings/
│ └── page.tsx → /dashboard/settings
└── analytics/
└── page.tsx → /dashboard/analytics
Dashboard routes get the root layout (TopNav, Footer) AND the dashboard layout (Sidebar):
// src/app/dashboard/layout.tsx
import { redirect } from "next/navigation";
import { getSession } from "@/lib/auth";
export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
const user = await getSession();
if (!user) redirect("/login"); // Protect all dashboard routes
return (
<div className="flex min-h-screen">
<DashboardSidebar user={user} />
<main className="flex-1 bg-gray-50">
{children}
</main>
</div>
);
}
Now /dashboard, /dashboard/settings, and /dashboard/analytics all have the sidebar, and they're all protected by the auth check — without any code in the page components.
Layouts Are Server Components
Layouts run on the server by default. This means you can fetch data in layouts, check authentication, read cookies, and do database queries:
// src/app/courses/[courseSlug]/layout.tsx
import { db } from "@/lib/db";
import { notFound } from "next/navigation";
import CourseSidebar from "@/components/CourseSidebar";
interface Props {
children: React.ReactNode;
params: { courseSlug: string };
}
export default async function CourseLayout({ children, params }: Props) {
const course = await db.course.findUnique({
where: { slug: params.courseSlug },
include: {
lessons: { orderBy: { order: "asc" } },
},
});
if (!course) notFound();
return (
<div className="flex">
<CourseSidebar course={course} />
<div className="flex-1 p-8">
{children}
</div>
</div>
);
}
The course data is fetched once for all lesson pages under /courses/[courseSlug]/. Navigating between lessons doesn't re-fetch the course — the layout doesn't re-render.
The Metadata System
Each layout and page can export a metadata object or generateMetadata function. Next.js merges them from parent to child:
// src/app/layout.tsx
export const metadata: Metadata = {
title: { template: "%s | AiTechWorlds", default: "AiTechWorlds" },
description: "Learn React, Next.js, Python, and AI.",
icons: { icon: "/favicon.ico" },
};
// src/app/courses/page.tsx
export const metadata: Metadata = {
title: "Browse Courses", // Becomes: "Browse Courses | AiTechWorlds"
description: "Practical courses for modern developers.",
};
// src/app/courses/[slug]/page.tsx — dynamic metadata
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const course = await getCourse(params.slug);
return {
title: course.title, // Becomes: "React Complete Course | AiTechWorlds"
description: course.description,
openGraph: {
title: course.title,
description: course.description,
images: [{ url: course.coverImage, width: 1200, height: 630 }],
},
};
}
The %s in the template becomes the page's title. Without a title in the page, the default is used.
Route Groups
Group routes with a (name) folder that doesn't appear in the URL. This lets you apply different layouts to different route sections:
src/app/
├── (marketing)/
│ ├── layout.tsx → Marketing layout (clean, no sidebar)
│ ├── page.tsx → /
│ ├── about/
│ │ └── page.tsx → /about
│ └── pricing/
│ └── page.tsx → /pricing
└── (app)/
├── layout.tsx → App layout (sidebar, auth required)
├── dashboard/
│ └── page.tsx → /dashboard
└── settings/
└── page.tsx → /settings
The (marketing) and (app) folders are invisible in the URL. /about and /dashboard both exist at the top level, but they use different layouts.
Parallel Routes — Multiple Simultaneous Pages
Show two different pages in the same layout at the same time using @slot naming:
src/app/
├── layout.tsx
├── page.tsx
├── @modal/
│ └── (.)photos/[id]/
│ └── page.tsx → Renders as a modal overlay
└── photos/
├── page.tsx → /photos
└── [id]/
└── page.tsx → /photos/123 (full page)
// src/app/layout.tsx
export default function Layout({
children,
modal, // The @modal slot
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<>
{children}
{modal} {/* The modal renders here, on top */}
</>
);
}
Clicking a photo shows it in a modal overlay. Refreshing the URL shows it as a full page. This is Instagram-style routing — and it's all in the file system.
Template vs Layout
template.tsx is like layout.tsx but it re-mounts when the user navigates between child routes. Use it when you need fresh state on every navigation:
// src/app/dashboard/template.tsx
export default function DashboardTemplate({ children }: { children: React.ReactNode }) {
// This unmounts and remounts on every navigation
// Use case: page entry animations, resetting scroll position
return <>{children}</>;
}
Most of the time you want layout.tsx (persists state). Use template.tsx when re-mounting is intentional.
Next lesson: Loading UI and Suspense — showing skeletons while data streams in.
Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises