Course
Template
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Template
Templates function similarly to layouts in web development, especially in React, by being passed to each child layout or page. However, unlike layouts, templates do not retain state.
When navigating between routes that use the same template, a new instance of each child is created. This results in component instances remounting and the DOM elements being recreated, thus not retaining state. This might seem abstract, so let’s break it down with an example.
To define a template, create a file named
template.js
and export a React component that receives a children
prop. For instance:
Create a
template.js
file in the app directory:// app/template.jsexport default function Template({ children }) { return <div>{children}</div>}
This is similar to using layouts, but with the crucial difference in state retention. If both
template.js
and layout.js
are in the same directory, the structure will be:<Layout> {/* The template requires a unique key */} <Template key={routeParam}>{children}</Template></Layout>
Here, the layout wraps the template, and the template wraps the page.
Templates are more suitable than layouts in certain cases:
- Functions that depend on useEffect and useState, like recording page visits or resetting user feedback forms.
- Modifying default framework behavior, such as showing fallback UI on each route change using Suspense.
For specific scenarios where templates are appropriate, refer to the article "What is the use of template.js in Next.js v14?"
Layouts VS. Templates
To illustrate the differences, here’s a project directory setup:
app└─ dashboard ├─ layout.js ├─ page.js ├─ template.js ├─ about │ └─ page.js └─ settings └─ page.js
Code for
dashboard/layout.js
:'use client';import { useState } from 'react';import Link from 'next/link';
export default function Layout({ children }) { const [count, setCount] = useState(0); return ( <div className="flex min-h-screen flex-col space-y-6"> <div className="container grid flex-1 gap-6 md:grid-cols-[200px_1fr]"> <aside className="hidden w-[200px] flex-col justify-between rounded-lg border border-black m-4 p-4 backdrop-blur-[1.5px] md:flex"> <div className="space-y-2 flex flex-col items-center"> <Link href="/dashboard/about" className="px-14 border border-black rounded-md" > About </Link> <br /> <Link href="/dashboard/settings" className="px-12 border border-black rounded-md" > Settings </Link> <h1>Layout count: {count}</h1> <button className=" px-11 border border-black rounded-md items-center justify-center bg-zinc-300 mt-2" onClick={() => setCount(count + 1)} > Increment </button> </div> </aside> <main className="flex w-full flex-1 flex-col">{children}</main> </div> </div> );}
Code for
dashboard/template.js
:'use client';
import { useState } from 'react';
export default function Template({ children }) { const [count, setCount] = useState(0); return ( <div className="m-4 w-full h-full border border-black rounded-lg flex flex-col items-center justify-center"> <h1>Template count: {count}</h1> <button className=" px-11 border border-black rounded-md items-center justify-center bg-zinc-300 mt-2" onClick={() => setCount(count + 1)} > Increment </button> {children} </div> );}
Code for
dashboard/page.js
:export default function Page() { return ( <div className="flex items-center justify-center mt-40"> <h1 className="text-3xl">Hello, Dashboard!</h1> </div> );}
Code for
dashboard/about/page.js
:export default function Page() { return ( <div className="flex items-center justify-center mt-40"> <h1 className="text-3xl">Hello, About!</h1> </div> );}
Code for
dashboard/settings/page.js
:export default function Page() { return ( <div className="flex items-center justify-center mt-40"> <h1 className="text-3xl">Hello, Settings!</h1> </div> );}
Try it out
When you click the Increment buttons, the counts will increase. However, switching routes (About or Settings) will show that the Layout count remains unchanged, while the Template count resets to 0. This demonstrates state retention.
Note: Refreshing the page will reset both Layout and Template counts to 0.