AiTechWorlds
AiTechWorlds
A layout in Next.js is a UI shell navigation, sidebar, footer that wraps a set of routes and stays mounted while only the page content inside it changes.
It behaves like a picture frame that stays on the wall while you swap the photo inside it. You don't rebuild the frame every time the picture changes; you just slide a new photo in. A Next.js layout is the frame, and each page is the photo swapping pages doesn't re render the navigation or footer around them.
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.
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/analyticsDashboard 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 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.
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.
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 → /settingsThe (marketing) and (app) folders are invisible in the URL. /about and /dashboard both exist at the top level, but they use different layouts.
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.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