Learn the rules of the Next.js root layout, when to use several root layouts, and how template.js differs from layout.js by remounting on every navigation.

Bishal Biswas
Self Learner
What you will learn
You have used layouts since Pages and Layouts. In this lesson you will look closer at the most important one, the root layout, and meet its lesser-known cousin, the template.
By the end you will know:
- The rules every root layout must follow, and why.
- How to have more than one root layout, and what that costs.
- How
template.jsdiffers fromlayout.js, and when that difference is useful.
The root layout
The root layout is the layout at the top of the app folder: app/layout.js. It wraps every single page of your site. Here is the one create-next-app made for Book Nook:
// app/layout.js
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({ children }) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
</html>
);
}
It is a normal React component, but it has a special job, so it comes with a few rules.
Rule 1: it must exist
The app folder must have a root layout. If it is missing when you run npm run dev, Next.js creates a basic app/layout.js for you. That is how important it is.
Rule 2: it must render html and body
In a plain React app, an index.html file holds the <html> and <body> tags. Next.js has no such file. The root layout is the HTML document, so it must return the <html> and <body> tags itself. Leave them out and the dev server shows:
Missing <html> and <body> tags in the root layout.
Read more at https://nextjs.org/docs/messages/missing-root-layout-tags
A good habit is to set lang on <html>, as above, so screen readers and search engines know the page language.
Rule 3: do not write head tags by hand
Do not add <head>, <title> or <meta> tags to the root layout yourself. Export a metadata object instead, as the generated file does. Next.js turns it into the right tags, merges them with the metadata of each page, and avoids duplicates. Let us give Book Nook its real name:
// app/layout.js
export const metadata = {
title: "Book Nook",
description: "A small online bookshop with hand-picked books.",
};
There is a whole lesson on this: Page Titles, Descriptions and Metadata.
Rule 4: shared UI goes here, page content does not
Anything every page needs (the site header, a footer, global CSS, fonts) belongs in the root layout. Book Nook’s root layout with a header:
// app/layout.js
import Header from "@/components/Header";
export default function RootLayout({ children }) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<Header />
{children}
</body>
</html>
);
}
(The font and metadata lines at the top of the file stay as they were.)
How layouts behave when you navigate
Layouts are kept when you move between pages. Click from /books to /books/dune and the root layout does not run again or rebuild its HTML. Only the part that changed (the page) is swapped. This is why navigation feels fast, and why any state inside a layout survives, like text typed into a search box in the header.
There is one more consequence: a layout does not get searchParams (see Reading Search Params from the URL), because it does not re-render when only the query string changes.
Multiple root layouts
Sometimes parts of a site look completely different. Imagine Book Nook gets a staff area at /admin with its own colours, fonts and navigation, sharing nothing with the shop. You can give each part its own root layout using route groups from Route Groups and Private Folders.
Remove app/layout.js and put a layout in each group instead:
app/
├── (shop)/
│ ├── layout.js root layout for the shop: <html> and <body>
│ ├── page.js /
│ └── books/page.js /books
└── (admin)/
├── layout.js root layout for staff: its own <html> and <body>
└── admin/page.js /admin
Any layout with no layout.js above it is a root layout, so both must follow the rules: each one renders its own <html> and <body>.
// app/(admin)/layout.js
import "../globals.css";
export const metadata = { title: "Book Nook Admin" };
export default function AdminRootLayout({ children }) {
return (
<html lang="en">
<body className="bg-zinc-900 text-white">{children}</body>
</html>
);
}
Things to know before you do this:
- Full page loads. Moving from a page in one root layout to a page in another (for example
/booksto/admin) is not a fast client-side navigation. The browser loads the whole new document, as if you typed the URL. - The home page must live in a group. With no
app/layout.js, the/page has to be inside one of the groups, likeapp/(shop)/page.js. - The 404 page. With no single root layout, a top-level
app/not-found.jshas nothing to render inside. Next.js has an experimentalglobal-not-found.jsfile for this case (see Not Found Pages and Redirects).
For most sites, one root layout plus a few nested layouts is enough. Book Nook keeps a single app/layout.js for the rest of the course.
Templates: layouts that start fresh
A template is a file called template.js. Like a layout, it wraps the pages below it and receives a children prop:
// app/books/template.js
export default function BooksTemplate({ children }) {
return <div>{children}</div>;
}
The difference is what happens on navigation:
layout.js |
template.js |
|
|---|---|---|
| On navigation | Kept. Same component, same DOM. | Thrown away and remounted. |
State inside it (useState) |
Kept | Reset to the starting value |
useEffect |
Runs once | Runs again after each navigation |
| HTML elements | Reused | Created fresh |
Think of a layout as the walls of a shop: they stay put while customers come and go. A template is like a welcome mat that is rolled out fresh for every new customer.
See it for yourself
Create a tiny Client Component with a text box. It uses useState, so it needs the 'use client' line (explained in Client Components and the use client Directive):
// components/NoteBox.js
'use client';
import { useState } from 'react';
export default function NoteBox() {
const [note, setNote] = useState('');
return (
<input
value={note}
onChange={(event) => setNote(event.target.value)}
placeholder="Type a note, then open a book"
/>
);
}
First put it in a layout for the books section:
// app/books/layout.js
import NoteBox from '@/components/NoteBox';
export default function BooksLayout({ children }) {
return (
<section>
<NoteBox />
{children}
</section>
);
}
Go to /books, type something, then click a book. The text is still there. The layout was kept.
Now rename app/books/layout.js to app/books/template.js (and the function to BooksTemplate). Do the same test: type, then click a book. The box is empty again. The template was remounted, so its state started over.
When is a template useful?
Most of the time you want a layout. Reach for a template when you want something to start over on every page change:
- An entrance animation that should play on every page, not only the first.
- An effect that should run on every page change, like logging a page view.
- A form or input that should be cleared when the visitor moves to another page.
Here is a fade-in for the books section. First, the animation in app/globals.css:
/* app/globals.css */
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in {
animation: fade-in 0.3s ease-out;
}
Then the template, which also logs each page view in the browser console:
// app/books/template.js
'use client';
import { useEffect } from 'react';
export default function BooksTemplate({ children }) {
useEffect(() => {
// Runs every time the template remounts, so once per page change
console.log('A books page was shown');
}, []);
return <div className="fade-in">{children}</div>;
}
Because the <div> is created fresh on every navigation, the browser plays the animation again each time.
Exactly when does a template remount?
A template remounts when the route segment below it changes. For app/books/template.js:
/booksto/books/dune: remounts./books/duneto/books/the-hobbit: remounts (the[id]value changed)./books?genre=classicto/books?genre=fantasy: does not remount. Search params never remount templates.
Where does a template sit?
In the same folder, Next.js nests the special files in this order, from outside to inside:
layout.js
└── template.js
└── error.js
└── loading.js
└── not-found.js
└── page.js
So a template sits just inside the layout of the same folder, and wraps everything else.
Common mistakes
Forgetting html or body in a root layout
Missing <html> and <body> tags in the root layout.
Every root layout, including each one in a multiple root layout setup, must return <html> with a <body> inside.
Adding a title tag to the root layout
A <title> written by hand can clash with the metadata from your pages. Export metadata instead.
Using usePathname in a template with Cache Components
You might try to log the path with usePathname() inside app/books/template.js. With Cache Components on, npm run build fails for the dynamic /books/[id] route:
Error: Route "/books/[id]": Next.js encountered URL data `usePathname()` in a Client Component outside of `<Suspense>`.
The path of a dynamic route is only known when a request arrives, so this hook must sit inside <Suspense>. A template wraps the page from outside any Suspense boundary, so keep URL hooks out of it, or move them into a small child component wrapped in <Suspense>.
Expecting a template to reset when the query string changes
Search params do not remount templates. If something must react to ?genre=..., read the value (with the searchParams prop or useSearchParams) and respond to it in your component.
Quick recap
- The root layout (
app/layout.js) wraps every page and must return<html>and<body>. - Use the
metadataexport for titles and descriptions, not hand-written<head>tags. - Layouts are kept during navigation, so their state survives and they do not get
searchParams. - You can have several root layouts in route groups. Each needs
<html>and<body>, and moving between them causes a full page load. template.jswraps pages like a layout, but remounts when the segment below it changes. State resets and effects run again.- Search params do not remount templates. Use templates for per-page animations, effects and resets.
// app/books/template.js: wraps every /books page and remounts on each page change
'use client'; // needed because we use useEffect
import { useEffect } from 'react';
export default function BooksTemplate({ children }) {
useEffect(() => {
// A layout would run this once. A template runs it after every navigation.
console.log('A books page was shown');
}, []);
// The div is created fresh each time, so the CSS animation replays
return <div className="fade-in">{children}</div>;
}
// --- app/globals.css: the animation used above
// @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
// .fade-in { animation: fade-in 0.3s ease-out; }
// --- app/layout.js: the root layout must render html and body
// export const metadata = { title: 'Book Nook', description: 'A small online bookshop.' };
// export default function RootLayout({ children }) {
// return (
// <html lang="en">
// <body>
// <Header />
// {children}
// </body>
// </html>
// );
// }
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready