Template
template.tsx wraps pages inside the layout chain - special file, no URL, nearest-wins.
Overview
template.tsx sits between the layout chain and the page. Like layout.tsx, it is a special file and does not create a URL. Unlike a layout, it is resolved per page scope and is a good place for effects that should run when navigating between pages.
| File | Creates URL? | Role |
|---|---|---|
| layout.tsx | No - special file | Wraps segment + children, receives params |
| template.tsx | No - special file | Wraps page inside layout chain, receives children |
| page.tsx | Yes | Route content |
children, not params.What is template.tsx?
A template.tsx file wraps each page in its scope. The nearest template with a default export is used (nearest-wins, same as loading and error).
- Between layout and page: renders inside the layout chain, around the page
- children only: templates receive
children, not routeparams - Folder-scoped: applies to that folder and descendants only
- No URL: special file - does not create a route
template.tsx vs layout.tsx
| Feature | layout.tsx | template.tsx |
|---|---|---|
| Creates URL? | No | No |
| Wraps | Segment + all children | Page inside layout chain |
| Props | Outlet / params | children |
| Typical use | Nav, sidebar, shared chrome | Page-level effects, transitions |
| Location | Any folder | Any folder |
Creating a Template
Create template.tsx in any folder. It must have a default export and receives children.
export default function Template({
children,
}: {
children: React.ReactNode
}) {
return <div>{children}</div>
}export default function Template({
children,
}: {
children: React.ReactNode
}) {
useEffect(() => {
// Useful for analytics or focus management
console.log('Template active')
}, [])
return <div>{children}</div>
}export default function DashboardTemplate({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="animate-in fade-in">
{children}
</div>
)
}html tag are ignored. Prefer simple wrappers around children.Use Cases
Prefer a template when you need page-scoped behavior without changing the persistent layout chrome.
| Use Case | Why template? |
|---|---|
| Page transitions | Wrap the page for enter/exit animation classes |
| Analytics / logging | Run effects around each page view |
| Focus management | Move focus when the page content changes |
| Reset local UI state | Keep layout state, re-init page-level UI |
File Naming - templates.tsx
On this docs site, the page file is named templates.tsx (plural) so it is not treated as the special template.tsx convention. The real special file in apps remains template.tsx.
| Real special file | Docs page file | Why? |
|---|---|---|
| app/template.tsx | app/docs/templates.tsx | Avoid special-file handling for the docs route |
| app/default.tsx | app/docs/defaults.tsx | Same idea for slot defaults |
Complete Example
Templates next to layouts, loading, and error - special files do not create URLs.
| File | Creates URL? | Purpose |
|---|---|---|
| app/layout.tsx | No | Root layout |
| app/template.tsx | No | Root template |
| app/page.tsx | Yes - / | Home page |
| app/dashboard/template.tsx | No | Dashboard page wrapper |
| app/dashboard/page.tsx | Yes - /dashboard | Dashboard page |
layout.tsx for shared chrome. Use template.tsx for page-level wrapping inside that chrome.