Layouts vs Templates
A layout persists across navigations — great for navbars and sidebars. A template re-mounts on every navigation — useful for animations or per-page analytics.
Every Next.js app requires a root layout.tsx in app/ which must include the <html> and <body> tags.
Layouts, Templates & the Root Layout
You've seen layout.tsx mentioned in the last two lessons. Now it's time to
understand exactly how layouts work, how they nest, what the root layout must contain,
and when to reach for a template.tsx instead.
This is one of the most important concepts in the App Router — get this right and your app's structure will feel clean and maintainable no matter how large it grows.
1. What Is a Layout?
A layout is a component that wraps a page (and any nested layouts below it). The key behaviour that makes layouts special is this:
Layouts do not re-render when you navigate between routes they wrap.
That means if your navbar is in a layout, it doesn't unmount and remount every time the user clicks a link. It stays alive — preserving scroll position, dropdown state, and any data it holds — while only the inner page content swaps out.
Create a layout by exporting a default function from a file named layout.tsx:
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<header>My Site Nav</header>
<main>{children}</main>
<footer>My Site Footer</footer>
</body>
</html>
);
}
The children prop is where Next.js injects the page content — or the next nested
layout. You don't import or reference the page directly; Next.js handles wiring it up.
2. The Root Layout — Rules & Requirements
Every Next.js App Router project must have a root layout at
app/layout.tsx. This is non-negotiable — Next.js will throw a build error
without it. There are specific rules for this file:
- It must include the
<html>and<body>tags — no other layout does. - It is a Server Component by default and should stay that way in most cases.
- It wraps every single page in your application.
- It's the right place for things that belong on every page: fonts, global CSS imports, analytics scripts, theme providers.
Here's a more complete real-world root layout:
// app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: {
template: "%s | My App",
default: "My App",
},
description: "Welcome to my Next.js application.",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
Notice the metadata export — we'll cover the Metadata API in depth in Module 4,
but putting a title.template in the root layout means every page that sets its
own title automatically gets " | My App" appended to it.
3. Nested Layouts
Any subfolder in app/ can have its own layout.tsx. That layout wraps
only the routes inside that folder — and it automatically sits inside its parent layout.
Let's build a real example — a site with a marketing section and a dashboard section, each with their own layout:
app/
├── layout.tsx ← Root layout (html, body, global nav)
├── page.tsx → /
├── about/
│ └── page.tsx → /about
└── dashboard/
├── layout.tsx ← Dashboard layout (sidebar)
├── page.tsx → /dashboard
├── analytics/
│ └── page.tsx → /dashboard/analytics
└── settings/
└── page.tsx → /dashboard/settings
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="dashboard-shell">
<aside className="sidebar">
<nav>
<a href="/dashboard">Overview</a>
<a href="/dashboard/analytics">Analytics</a>
<a href="/dashboard/settings">Settings</a>
</nav>
</aside>
<div className="dashboard-content">
{children}
</div>
</div>
);
}
When a user visits /dashboard/analytics, Next.js renders this tree:
RootLayout
└── DashboardLayout
└── AnalyticsPage
The root layout renders once. When the user clicks from
/dashboard/analytics to /dashboard/settings, only
AnalyticsPage swaps out for SettingsPage. The root layout
and the dashboard layout (including its sidebar) stay completely intact — no flicker,
no remount, no scroll jump.
Layouts Can Fetch Data Too
Since layouts are Server Components by default, they can fetch data just like pages can. A common pattern is fetching the current user in the dashboard layout so every dashboard page has access to it via the layout's rendered UI:
// app/dashboard/layout.tsx
import { getCurrentUser } from "@/lib/auth";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const user = await getCurrentUser();
return (
<div className="dashboard-shell">
<aside>
<p>Welcome, {user.name}</p>
<nav>...</nav>
</aside>
<main>{children}</main>
</div>
);
}
4. What Is a Template?
A template.tsx file looks identical to a layout — same props, same structure.
The difference is one fundamental behaviour:
Templates re-mount on every navigation. A new instance is created each time the user navigates to a route that the template wraps.
Layouts persist. Templates remount. That's the entire difference.
// app/dashboard/template.tsx (instead of layout.tsx)
export default function DashboardTemplate({
children,
}: {
children: React.ReactNode;
}) {
return <div>{children}</div>;
}
When to Use a Template Instead of a Layout
Because templates remount, they trigger:
- CSS enter/exit animations — if you want a page-transition animation to play on every navigation, a template gives you a fresh mount to trigger it from.
useEffectre-runs — since the component remounts, effects run again. Useful for logging a page view or resetting some state on every visit.- State resets — form state, scroll position, or any local state inside the template resets on each navigation, which is sometimes exactly what you want.
For most features — navbars, sidebars, persistent UI — you want a layout. Reach for a template only when you specifically need the remounting behaviour.
5. Combining Layouts and Templates
You can use both in the same folder. Next.js applies them in this order:
Layout → Template → Page
So the layout persists (sidebar stays), but the template remounts (page transition animation plays), and the page renders fresh content. This is a clean pattern for dashboards with animated page transitions:
app/
└── dashboard/
├── layout.tsx ← Persistent sidebar
├── template.tsx ← Animated wrapper that remounts
└── page.tsx
6. Passing Data from Layouts to Pages
One thing that trips people up: you can't directly pass props from a layout to its
child pages. The children prop is fully controlled by Next.js — you
can't add extra props to it.
If you need to share data between a layout and its pages, you have two good options:
Option A: Fetch the same data in both
Because Next.js deduplicates identical fetch() calls within a single render
(request memoization), fetching the same URL in a layout and a page doesn't result in two
network requests — it's one request, cached and shared.
// app/dashboard/layout.tsx
const user = await getUser(); // fetch #1
// app/dashboard/page.tsx
const user = await getUser(); // same fetch — deduplicated, no extra request
Option B: Use React Context (Client Components only)
If you need client-side sharing, create a Context provider as a Client Component and
wrap children in it inside your layout:
// app/dashboard/_components/UserProvider.tsx
"use client";
import { createContext, useContext } from "react";
const UserContext = createContext<{ name: string } | null>(null);
export function UserProvider({
user,
children,
}: {
user: { name: string };
children: React.ReactNode;
}) {
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
}
export function useUser() {
return useContext(UserContext);
}
// app/dashboard/layout.tsx (Server Component)
import { UserProvider } from "./_components/UserProvider";
import { getCurrentUser } from "@/lib/auth";
export default async function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
const user = await getCurrentUser(); // runs on server
return (
<UserProvider user={user}>
{children}
</UserProvider>
);
}
// app/dashboard/page.tsx (or any nested Client Component)
"use client";
import { useUser } from "./_components/UserProvider";
export default function DashboardPage() {
const user = useUser();
return <h1>Hello, {user?.name}!</h1>;
}
This is the standard pattern for passing server-fetched data into client-side context —
the layout fetches on the server, wraps children in a provider, and any Client Component
in the tree can consume it with useContext.
7. Multiple Root Layouts with Route Groups
Here's a powerful technique: use route groups to create multiple root layouts
— effectively two completely different app shells with different <html> and
<body> structures.
app/
├── (marketing)/
│ ├── layout.tsx ← Has its own html/body — marketing design
│ └── page.tsx
└── (app)/
├── layout.tsx ← Has its own html/body — app design
└── dashboard/
└── page.tsx
Important: when using multiple root layouts like this, you remove
app/layout.tsx at the top level. Each route group's layout becomes its own
root — they must each include <html> and <body> tags.
This is perfect for SaaS apps where the marketing site and the logged-in app look completely different — different fonts, different colour schemes, different navigation structures.
8. Common Gotchas
-
Only the root layout has
<html>and<body>. Nested layouts must not include these tags — you'll get a React hydration error if they do. -
Layouts can't access their own route's
paramsdirectly. If you need the current route's dynamic segment inside a layout, pass it viaparamsprop:layout.tsxreceives the sameparamsandsearchParamsprops as its siblingpage.tsx. -
Don't put interactive components in layouts without
"use client". Layouts are Server Components by default. Adding anonClickhandler without marking the component as a Client Component will cause a runtime error. - Confusing layout and template use cases. A good rule of thumb: if you'd be annoyed that your component "resets" on every navigation, use a layout. If you'd be annoyed that it doesn't reset, use a template.
Key Takeaways
- A layout wraps pages and persists across navigations — perfect for navbars, sidebars, and shells.
- The root layout at
app/layout.tsxis required, must include<html>and<body>, and wraps every page. - Nested layouts compose automatically — inner layouts sit inside outer ones without any manual wiring.
- A template looks like a layout but remounts on every navigation — use it for animations, per-page effects, or intentional state resets.
- You can't pass props from a layout directly to its pages — use fetch deduplication or React Context instead.
- Route groups allow multiple root layouts for completely different app shells.
Next up: Lesson 104 — Navigation with <Link> and useRouter.
You'll learn how client-side navigation works, how prefetching makes your app feel instant,
and how to navigate programmatically from event handlers.