Files
dfbc3dc6b7 Loading layout docs update (#90787)
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:

## For Contributors

### Improving Documentation

- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide

### Fixing a bug

- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md

### Adding a feature

- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md


## For Maintainers

- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change

### What?
This PR updates the documentation to clarify the interaction between
`loading.js` and `layout.js`.

### Why?
To address a common point of confusion and potential blocking navigation
issues when layouts are dynamic. It provides clear guidance on how
`loading.js` behaves relative to layouts and how to use `<Suspense>` for
dynamic layout parts.

### How?
- Added a "Good to know" callout in `loading.mdx` explaining that
`loading.js` does not wrap the layout.
- Introduced a new "Interaction with `loading.js`" section under Caveats
in `layout.mdx`, including an explanation and a code example for
wrapping dynamic layout components with `<Suspense>`.
- Added a brief "Good to know" callout in `fetching-data.mdx` for
consistency.

Closes NEXT-
Fixes #
-->

---
[Slack
Thread](https://vercel.slack.com/archives/C05KYT5S9FF/p1772239839900129?thread_ts=1772239839.900129&cid=C05KYT5S9FF)

<p><a
href="https://cursor.com/agents/bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</p>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Joseph Chamochumbi <[email protected]>
Co-authored-by: Delba de Oliveira <[email protected]>
2026-03-05 10:46:26 +01:00

163 lines
5.1 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: template.js
description: API Reference for the template.js file.
---
A **template** file is similar to a [layout](/docs/app/getting-started/layouts-and-pages#creating-a-layout) in that it wraps a layout or page. Unlike layouts that persist across routes and maintain state, templates are given a unique key, meaning children Client Components reset their state on navigation.
They are useful when you need to:
- Resynchronize `useEffect` on navigation.
- Reset the state of a child Client Components on navigation. For example, an input field.
- To change default framework behavior. For example, Suspense boundaries inside layouts only show a fallback on first load, while templates show it on every navigation.
## Convention
A template can be defined by exporting a default React component from a `template.js` file. The component should accept a `children` prop.
<Image
alt="template.js special file"
srcLight="/docs/light/template-special-file.png"
srcDark="/docs/dark/template-special-file.png"
width="1600"
height="444"
/>
```tsx filename="app/template.tsx" switcher
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
```
```jsx filename="app/template.js" switcher
export default function Template({ children }) {
return <div>{children}</div>
}
```
In terms of nesting, `template.js` is rendered between a layout and its children. Here's a simplified output:
```jsx filename="Output"
<Layout>
{/* Note that the template is given a unique key. */}
<Template key={routeParam}>{children}</Template>
</Layout>
```
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `template.js` renders between `layout.js` and `error.js`. It wraps `error.js`, `loading.js`, `not-found.js`, and `page.js`, but does **not** wrap the `layout.js` in the same segment.
## Props
### `children` (required)
Template accepts a `children` prop.
```jsx filename="Output"
<Layout>
{/* Note that the template is automatically given a unique key. */}
<Template key={routeParam}>{children}</Template>
</Layout>
```
## Behavior
- **Server Components**: By default, templates are Server Components.
- **With navigation**: Templates receive a unique key for their own segment level. They remount when that segment (including its dynamic params) changes. Navigations within deeper segments do not remount higher-level templates. Search params do not trigger remounts.
- **State reset**: Any Client Component inside the template will reset its state on navigation.
- **Effect re-run**: Effects like `useEffect` will re-synchronize as the component remounts.
- **DOM reset**: DOM elements inside the template are fully recreated.
### Templates during navigation and remounting
This section illustrates how templates behave during navigation. It shows, step by step, which templates remount on each route change and why.
Using this project tree:
```
app
├── about
│   ├── page.tsx
├── blog
│   ├── [slug]
│   │   └── page.tsx
│   ├── page.tsx
│   └── template.tsx
├── layout.tsx
├── page.tsx
└── template.tsx
```
Starting at `/`, the React tree looks roughly like this.
> Note: The `key` values shown in the examples are illustrative, the values in your application may differ.
```jsx filename="Output"
<RootLayout>
{/* app/template.tsx */}
<Template key="/">
<Page />
</Template>
</RootLayout>
```
Navigating to `/about` (first segment changes), the root template key changes, it remounts:
```jsx filename="Output"
<RootLayout>
{/* app/template.tsx */}
<Template key="/about">
<AboutPage />
</Template>
</RootLayout>
```
Navigating to `/blog` (first segment changes), the root template key changes, it remounts and the blog-level template mounts:
```jsx filename="Output"
<RootLayout>
{/* app/template.tsx (root) */}
<Template key="/blog">
{/* app/blog/template.tsx */}
<Template key="/blog">
<BlogIndexPage />
</Template>
</Template>
</RootLayout>
```
Navigating within the same first segment to `/blog/first-post` (child segment changes), the root template key doesn't change, but the blog-level template key changes, it remounts:
```jsx filename="Output"
<RootLayout>
{/* app/template.tsx (root) */}
<Template key="/blog">
{/* app/blog/template.tsx */}
{/* remounts because the child segment at this level changed */}
<Template key="/blog/first-post">
<BlogPostPage slug="first-post" />
</Template>
</Template>
</RootLayout>
```
Navigating to `/blog/second-post` (same first segment, different child segment), the root template key doesn't change, but the blog-level template key changes, it remounts again:
```jsx filename="Output"
<RootLayout>
{/* app/template.tsx (root) */}
<Template key="/blog">
{/* app/blog/template.tsx */}
{/* remounts again due to changed child segment */}
<Template key="/blog/second-post">
<BlogPostPage slug="second-post" />
</Template>
</Template>
</RootLayout>
```
## Version History
| Version | Changes |
| --------- | ---------------------- |
| `v13.0.0` | `template` introduced. |