Recipe-app main

This commit is contained in:
2026-04-09 09:14:39 +02:00
commit 962f4e4be5
10015 changed files with 2445177 additions and 0 deletions
@@ -0,0 +1,268 @@
---
title: favicon, icon, and apple-icon
description: API Reference for the Favicon, Icon and Apple Icon file conventions.
---
The `favicon`, `icon`, or `apple-icon` file conventions allow you to set icons for your application.
They are useful for adding app icons that appear in places like web browser tabs, phone home screens, and search engine results.
There are two ways to set app icons:
- [Using image files (.ico, .jpg, .png)](#image-files-ico-jpg-png)
- [Using code to generate an icon (.js, .ts, .tsx)](#generate-icons-using-code-js-ts-tsx)
## Image files (.ico, .jpg, .png)
Use an image file to set an app icon by placing a `favicon`, `icon`, or `apple-icon` image file within your `/app` directory.
The `favicon` image can only be located in the top level of `app/`.
Next.js will evaluate the file and automatically add the appropriate tags to your app's `<head>` element.
| File convention | Supported file types | Valid locations |
| --------------------------- | --------------------------------------- | --------------- |
| [`favicon`](#favicon) | `.ico` | `app/` |
| [`icon`](#icon) | `.ico`, `.jpg`, `.jpeg`, `.png`, `.svg` | `app/**/*` |
| [`apple-icon`](#apple-icon) | `.jpg`, `.jpeg`, `.png` | `app/**/*` |
### `favicon`
Add a `favicon.ico` image file to the root `/app` route segment.
```html filename="<head> output"
<link rel="icon" href="/favicon.ico" sizes="any" />
```
### `icon`
Add an `icon.(ico|jpg|jpeg|png|svg)` image file.
```html filename="<head> output"
<link
rel="icon"
href="/icon?<generated>"
type="image/<generated>"
sizes="<generated>"
/>
```
### `apple-icon`
Add an `apple-icon.(jpg|jpeg|png)` image file.
```html filename="<head> output"
<link
rel="apple-touch-icon"
href="/apple-icon?<generated>"
type="image/<generated>"
sizes="<generated>"
/>
```
> **Good to know**:
>
> - You can set multiple icons by adding a number suffix to the file name. For example, `icon1.png`, `icon2.png`, etc. Numbered files will sort lexically.
> - Favicons can only be set in the root `/app` segment. If you need more granularity, you can use [`icon`](#icon).
> - The appropriate `<link>` tags and attributes such as `rel`, `href`, `type`, and `sizes` are determined by the icon type and metadata of the evaluated file.
> - For example, a 32 by 32px `.png` file will have `type="image/png"` and `sizes="32x32"` attributes.
> - `sizes="any"` is added to icons when the extension is `.svg` or the image size of the file is not determined. More details in this [favicon handbook](https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs).
## Generate icons using code (.js, .ts, .tsx)
In addition to using [literal image files](#image-files-ico-jpg-png), you can programmatically **generate** icons using code.
Generate an app icon by creating an `icon` or `apple-icon` route that default exports a function.
| File convention | Supported file types |
| --------------- | -------------------- |
| `icon` | `.js`, `.ts`, `.tsx` |
| `apple-icon` | `.js`, `.ts`, `.tsx` |
The easiest way to generate an icon is to use the [`ImageResponse`](/docs/app/api-reference/functions/image-response) API from `next/og`.
```tsx filename="app/icon.tsx" switcher
import { ImageResponse } from 'next/og'
// Image metadata
export const size = {
width: 32,
height: 32,
}
export const contentType = 'image/png'
// Image generation
export default function Icon() {
return new ImageResponse(
(
// ImageResponse JSX element
<div
style={{
fontSize: 24,
background: 'black',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
}}
>
A
</div>
),
// ImageResponse options
{
// For convenience, we can re-use the exported icons size metadata
// config to also set the ImageResponse's width and height.
...size,
}
)
}
```
```jsx filename="app/icon.js" switcher
import { ImageResponse } from 'next/og'
// Image metadata
export const size = {
width: 32,
height: 32,
}
export const contentType = 'image/png'
// Image generation
export default function Icon() {
return new ImageResponse(
(
// ImageResponse JSX element
<div
style={{
fontSize: 24,
background: 'black',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
}}
>
A
</div>
),
// ImageResponse options
{
// For convenience, we can re-use the exported icons size metadata
// config to also set the ImageResponse's width and height.
...size,
}
)
}
```
```html filename="<head> output"
<link rel="icon" href="/icon?<generated>" type="image/png" sizes="32x32" />
```
> **Good to know**:
>
> - By default, generated icons are [**statically optimized**](/docs/app/glossary#prerendering) (generated at build time and cached) unless they use [Request-time APIs](/docs/app/glossary#request-time-apis) or uncached data.
> - You can generate multiple icons in the same file using [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata).
> - You cannot generate a `favicon` icon. Use [`icon`](#icon) or a [favicon.ico](#favicon) file instead.
> - App icons are special Route Handlers that are cached by default unless they use a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
### Props
The default export function receives the following props:
#### `params` (optional)
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) object from the root segment down to the segment `icon` or `apple-icon` is colocated in.
> **Good to know**: If you use [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata), the function will also receive an `id` prop that is a promise resolving to the `id` value from one of the items returned by `generateImageMetadata`.
```tsx filename="app/shop/[slug]/icon.tsx" switcher
export default async function Icon({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// ...
}
```
```jsx filename="app/shop/[slug]/icon.js" switcher
export default async function Icon({ params }) {
const { slug } = await params
// ...
}
```
| Route | URL | `params` |
| ------------------------------- | ----------- | ---------------------------------- |
| `app/shop/icon.js` | `/shop` | `undefined` |
| `app/shop/[slug]/icon.js` | `/shop/1` | `Promise<{ slug: '1' }>` |
| `app/shop/[tag]/[item]/icon.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` |
### Returns
The default export function should return a `Blob` | `ArrayBuffer` | `TypedArray` | `DataView` | `ReadableStream` | `Response`.
> **Good to know**: `ImageResponse` satisfies this return type.
### Config exports
You can optionally configure the icon's metadata by exporting `size` and `contentType` variables from the `icon` or `apple-icon` route.
| Option | Type |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| [`size`](#size) | `{ width: number; height: number }` |
| [`contentType`](#contenttype) | `string` - [image MIME type](https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types) |
#### `size`
```tsx filename="icon.tsx | apple-icon.tsx" switcher
export const size = { width: 32, height: 32 }
export default function Icon() {}
```
```jsx filename="icon.js | apple-icon.js" switcher
export const size = { width: 32, height: 32 }
export default function Icon() {}
```
```html filename="<head> output"
<link rel="icon" sizes="32x32" />
```
#### `contentType`
```tsx filename="icon.tsx | apple-icon.tsx" switcher
export const contentType = 'image/png'
export default function Icon() {}
```
```jsx filename="icon.js | apple-icon.js" switcher
export const contentType = 'image/png'
export default function Icon() {}
```
```html filename="<head> output"
<link rel="icon" type="image/png" />
```
#### Route Segment Config
`icon` and `apple-icon` are specialized [Route Handlers](/docs/app/api-reference/file-conventions/route) that can use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) options as Pages and Layouts.
## Version History
| Version | Changes |
| --------- | ---------------------------------------------------- |
| `v16.0.0` | `params` is now a promise that resolves to an object |
| `v13.3.0` | `favicon` `icon` and `apple-icon` introduced |
@@ -0,0 +1,16 @@
---
title: Metadata Files API Reference
nav_title: Metadata Files
description: API documentation for the metadata file conventions.
---
This section of the docs covers **Metadata file conventions**. File-based metadata can be defined by adding special metadata files to route segments.
Each file convention can be defined using a static file (e.g. `opengraph-image.jpg`), or a dynamic variant that uses code to generate the file (e.g. `opengraph-image.js`).
Once a file is defined, Next.js will automatically serve the file (with hashes in production for caching) and update the relevant head elements with the correct metadata, such as the asset's URL, file type, and image size.
> **Good to know**:
>
> - Special Route Handlers like [`sitemap.ts`](/docs/app/api-reference/file-conventions/metadata/sitemap), [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`icon.tsx`](/docs/app/api-reference/file-conventions/metadata/app-icons), and other [metadata files](/docs/app/api-reference/file-conventions/metadata) are cached by default.
> - If using along with [`proxy.ts`](/docs/app/api-reference/file-conventions/proxy), [configure the matcher](/docs/app/api-reference/file-conventions/proxy#matcher) to exclude the metadata files.
@@ -0,0 +1,72 @@
---
title: manifest.json
description: API Reference for manifest.json file.
---
Add or generate a `manifest.(json|webmanifest)` file that matches the [Web Manifest Specification](https://developer.mozilla.org/docs/Web/Manifest) in the **root** of `app` directory to provide information about your web application for the browser.
## Static Manifest file
```json filename="app/manifest.json | app/manifest.webmanifest"
{
"name": "My Next.js Application",
"short_name": "Next.js App",
"description": "An application built with Next.js",
"start_url": "/"
// ...
}
```
## Generate a Manifest file
Add a `manifest.js` or `manifest.ts` file that returns a [`Manifest` object](#manifest-object).
> Good to know: `manifest.js` is a special Route Handlers that is cached by default unless it uses a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
```ts filename="app/manifest.ts" switcher
import type { MetadataRoute } from 'next'
export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Next.js App',
short_name: 'Next.js App',
description: 'Next.js App',
start_url: '/',
display: 'standalone',
background_color: '#fff',
theme_color: '#fff',
icons: [
{
src: '/favicon.ico',
sizes: 'any',
type: 'image/x-icon',
},
],
}
}
```
```js filename="app/manifest.js" switcher
export default function manifest() {
return {
name: 'Next.js App',
short_name: 'Next.js App',
description: 'Next.js App',
start_url: '/',
display: 'standalone',
background_color: '#fff',
theme_color: '#fff',
icons: [
{
src: '/favicon.ico',
sizes: 'any',
type: 'image/x-icon',
},
],
}
}
```
### Manifest Object
The manifest object contains an extensive list of options that may be updated due to new web standards. For information on all the current options, refer to the `MetadataRoute.Manifest` type in your code editor if using [TypeScript](/docs/app/api-reference/config/typescript#ide-plugin) or see the [MDN](https://developer.mozilla.org/docs/Web/Manifest) docs.
@@ -0,0 +1,529 @@
---
title: opengraph-image and twitter-image
description: API Reference for the Open Graph Image and Twitter Image file conventions.
---
The `opengraph-image` and `twitter-image` file conventions allow you to set Open Graph and Twitter images for a route segment.
They are useful for setting the images that appear on social networks and messaging apps when a user shares a link to your site.
There are two ways to set Open Graph and Twitter images:
- [Using image files (.jpg, .png, .gif)](#image-files-jpg-png-gif)
- [Using code to generate images (.js, .ts, .tsx)](#generate-images-using-code-js-ts-tsx)
## Image files (.jpg, .png, .gif)
Use an image file to set a route segment's shared image by placing an `opengraph-image` or `twitter-image` image file in the segment.
Next.js will evaluate the file and automatically add the appropriate tags to your app's `<head>` element.
| File convention | Supported file types |
| ----------------------------------------------- | ------------------------------- |
| [`opengraph-image`](#opengraph-image) | `.jpg`, `.jpeg`, `.png`, `.gif` |
| [`twitter-image`](#twitter-image) | `.jpg`, `.jpeg`, `.png`, `.gif` |
| [`opengraph-image.alt`](#opengraph-imagealttxt) | `.txt` |
| [`twitter-image.alt`](#twitter-imagealttxt) | `.txt` |
> **Good to know**:
>
> The `twitter-image` file size must not exceed [5MB](https://developer.x.com/en/docs/x-for-websites/cards/overview/summary), and the `opengraph-image` file size must not exceed [8MB](https://developers.facebook.com/docs/sharing/webmasters/images). If the image file size exceeds these limits, the build will fail.
### `opengraph-image`
Add an `opengraph-image.(jpg|jpeg|png|gif)` image file to any route segment.
```html filename="<head> output"
<meta property="og:image" content="<generated>" />
<meta property="og:image:type" content="<generated>" />
<meta property="og:image:width" content="<generated>" />
<meta property="og:image:height" content="<generated>" />
```
### `twitter-image`
Add a `twitter-image.(jpg|jpeg|png|gif)` image file to any route segment.
```html filename="<head> output"
<meta name="twitter:image" content="<generated>" />
<meta name="twitter:image:type" content="<generated>" />
<meta name="twitter:image:width" content="<generated>" />
<meta name="twitter:image:height" content="<generated>" />
```
### `opengraph-image.alt.txt`
Add an accompanying `opengraph-image.alt.txt` file in the same route segment as the `opengraph-image.(jpg|jpeg|png|gif)` image it's alt text.
```txt filename="opengraph-image.alt.txt"
About Acme
```
```html filename="<head> output"
<meta property="og:image:alt" content="About Acme" />
```
### `twitter-image.alt.txt`
Add an accompanying `twitter-image.alt.txt` file in the same route segment as the `twitter-image.(jpg|jpeg|png|gif)` image it's alt text.
```txt filename="twitter-image.alt.txt"
About Acme
```
```html filename="<head> output"
<meta property="twitter:image:alt" content="About Acme" />
```
## Generate images using code (.js, .ts, .tsx)
In addition to using [literal image files](#image-files-jpg-png-gif), you can programmatically **generate** images using code.
Generate a route segment's shared image by creating an `opengraph-image` or `twitter-image` route that default exports a function.
| File convention | Supported file types |
| ----------------- | -------------------- |
| `opengraph-image` | `.js`, `.ts`, `.tsx` |
| `twitter-image` | `.js`, `.ts`, `.tsx` |
> **Good to know**:
>
> - By default, generated images are [**statically optimized**](/docs/app/glossary#prerendering) (generated at build time and cached) unless they use [Request-time APIs](/docs/app/glossary#request-time-apis) or uncached data.
> - You can generate multiple Images in the same file using [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata).
> - `opengraph-image.js` and `twitter-image.js` are special Route Handlers that is cached by default unless it uses a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
The easiest way to generate an image is to use the [ImageResponse](/docs/app/api-reference/functions/image-response) API from `next/og`.
```tsx filename="app/about/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
// Image metadata
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Image generation
export default async function Image() {
// Font loading, process.cwd() is Next.js project directory
const interSemiBold = await readFile(
join(process.cwd(), 'assets/Inter-SemiBold.ttf')
)
return new ImageResponse(
(
// ImageResponse JSX element
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
About Acme
</div>
),
// ImageResponse options
{
// For convenience, we can re-use the exported opengraph-image
// size config to also set the ImageResponse's width and height.
...size,
fonts: [
{
name: 'Inter',
data: interSemiBold,
style: 'normal',
weight: 400,
},
],
}
)
}
```
```jsx filename="app/about/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
// Image metadata
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Image generation
export default async function Image() {
// Font loading, process.cwd() is Next.js project directory
const interSemiBold = await readFile(
join(process.cwd(), 'assets/Inter-SemiBold.ttf')
)
return new ImageResponse(
(
// ImageResponse JSX element
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
About Acme
</div>
),
// ImageResponse options
{
// For convenience, we can re-use the exported opengraph-image
// size config to also set the ImageResponse's width and height.
...size,
fonts: [
{
name: 'Inter',
data: interSemiBold,
style: 'normal',
weight: 400,
},
],
}
)
}
```
```html filename="<head> output"
<meta property="og:image" content="<generated>" />
<meta property="og:image:alt" content="About Acme" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
```
### Props
The default export function receives the following props:
#### `params` (optional)
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) object from the root segment down to the segment `opengraph-image` or `twitter-image` is colocated in.
> **Good to know**: If you use [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata), the function will also receive an `id` prop that is a promise resolving to the `id` value from one of the items returned by `generateImageMetadata`.
```tsx filename="app/shop/[slug]/opengraph-image.tsx" switcher
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// ...
}
```
```jsx filename="app/shop/[slug]/opengraph-image.js" switcher
export default async function Image({ params }) {
const { slug } = await params
// ...
}
```
| Route | URL | `params` |
| ------------------------------------------ | ----------- | ---------------------------------- |
| `app/shop/opengraph-image.js` | `/shop` | `undefined` |
| `app/shop/[slug]/opengraph-image.js` | `/shop/1` | `Promise<{ slug: '1' }>` |
| `app/shop/[tag]/[item]/opengraph-image.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` |
### Returns
The default export function should return a `Blob` | `ArrayBuffer` | `TypedArray` | `DataView` | `ReadableStream` | `Response`.
> **Good to know**: `ImageResponse` satisfies this return type.
### Config exports
You can optionally configure the image's metadata by exporting `alt`, `size`, and `contentType` variables from `opengraph-image` or `twitter-image` route.
| Option | Type |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------- |
| [`alt`](#alt) | `string` |
| [`size`](#size) | `{ width: number; height: number }` |
| [`contentType`](#contenttype) | `string` - [image MIME type](https://developer.mozilla.org/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types) |
#### `alt`
```tsx filename="opengraph-image.tsx | twitter-image.tsx" switcher
export const alt = 'My images alt text'
export default function Image() {}
```
```jsx filename="opengraph-image.js | twitter-image.js" switcher
export const alt = 'My images alt text'
export default function Image() {}
```
```html filename="<head> output"
<meta property="og:image:alt" content="My images alt text" />
```
#### `size`
```tsx filename="opengraph-image.tsx | twitter-image.tsx" switcher
export const size = { width: 1200, height: 630 }
export default function Image() {}
```
```jsx filename="opengraph-image.js | twitter-image.js" switcher
export const size = { width: 1200, height: 630 }
export default function Image() {}
```
```html filename="<head> output"
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
```
#### `contentType`
```tsx filename="opengraph-image.tsx | twitter-image.tsx" switcher
export const contentType = 'image/png'
export default function Image() {}
```
```jsx filename="opengraph-image.js | twitter-image.js" switcher
export const contentType = 'image/png'
export default function Image() {}
```
```html filename="<head> output"
<meta property="og:image:type" content="image/png" />
```
#### Route Segment Config
`opengraph-image` and `twitter-image` are specialized [Route Handlers](/docs/app/api-reference/file-conventions/route) that can use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) options as Pages and Layouts.
### Examples
#### Using external data
This example uses the `params` object and external data to generate the image.
> **Good to know**:
> By default, this generated image will be statically optimized. You can configure the individual `fetch` [`options`](/docs/app/api-reference/functions/fetch) or route segments [options](/docs/app/guides/caching-without-cache-components#route-segment-config-revalidate) to change this behavior.
```tsx filename="app/posts/[slug]/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
export default async function Image({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetch(`https://.../posts/${slug}`).then((res) =>
res.json()
)
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
),
{
...size,
}
)
}
```
```jsx filename="app/posts/[slug]/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
export default async function Image({ params }) {
const { slug } = await params
const post = await fetch(`https://.../posts/${slug}`).then((res) =>
res.json()
)
return new ImageResponse(
(
<div
style={{
fontSize: 48,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{post.title}
</div>
),
{
...size,
}
)
}
```
#### Using Node.js runtime with local assets
These examples use the Node.js runtime to fetch a local image from the file system and pass it to the `<img>` `src` attribute, either as a base64 string or an `ArrayBuffer`. Place the local asset relative to the project root, not the example source file.
```tsx filename="app/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'
export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'), 'base64')
const logoSrc = `data:image/png;base64,${logoData}`
return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}
```
```jsx filename="app/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'
export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'), 'base64')
const logoSrc = `data:image/png;base64,${logoData}`
return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}
```
Passing an `ArrayBuffer` to the `src` attribute of an `<img>` element is not part of the HTML spec. The rendering engine used by `next/og` supports it, but because TypeScript definitions follow the spec, you need a `@ts-expect-error` directive or similar to use this [feature](https://github.com/vercel/satori/issues/606#issuecomment-2144000453).
```tsx filename="app/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'
export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'))
const logoSrc = Uint8Array.from(logoData).buffer
return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{/* @ts-expect-error Satori accepts ArrayBuffer/typed arrays for <img src> at runtime */}
<img src={logoSrc} height="100" />
</div>
)
)
}
```
```jsx filename="app/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'
export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'))
const logoSrc = Uint8Array.from(logoData).buffer
return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}
```
## Version History
| Version | Changes |
| --------- | ---------------------------------------------------- |
| `v16.0.0` | `params` is now a promise that resolves to an object |
| `v13.3.0` | `opengraph-image` and `twitter-image` introduced. |
@@ -0,0 +1,148 @@
---
title: robots.txt
description: API Reference for robots.txt file.
---
Add or generate a `robots.txt` file that matches the [Robots Exclusion Standard](https://en.wikipedia.org/wiki/Robots.txt#Standard) in the **root** of `app` directory to tell search engine crawlers which URLs they can access on your site.
## Static `robots.txt`
```txt filename="app/robots.txt"
User-Agent: *
Allow: /
Disallow: /private/
Sitemap: https://acme.com/sitemap.xml
```
## Generate a Robots file
Add a `robots.js` or `robots.ts` file that returns a [`Robots` object](#robots-object).
> **Good to know**: `robots.js` is a special Route Handler that is cached by default unless it uses a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
```ts filename="app/robots.ts" switcher
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: '/private/',
},
sitemap: 'https://acme.com/sitemap.xml',
}
}
```
```js filename="app/robots.js" switcher
export default function robots() {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: '/private/',
},
sitemap: 'https://acme.com/sitemap.xml',
}
}
```
Output:
```txt
User-Agent: *
Allow: /
Disallow: /private/
Sitemap: https://acme.com/sitemap.xml
```
### Customizing specific user agents
You can customize how individual search engine bots crawl your site by passing an array of user agents to the `rules` property. For example:
```ts filename="app/robots.ts" switcher
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: 'Googlebot',
allow: ['/'],
disallow: '/private/',
},
{
userAgent: ['Applebot', 'Bingbot'],
disallow: ['/'],
},
],
sitemap: 'https://acme.com/sitemap.xml',
}
}
```
```js filename="app/robots.js" switcher
export default function robots() {
return {
rules: [
{
userAgent: 'Googlebot',
allow: ['/'],
disallow: ['/private/'],
},
{
userAgent: ['Applebot', 'Bingbot'],
disallow: ['/'],
},
],
sitemap: 'https://acme.com/sitemap.xml',
}
}
```
Output:
```txt
User-Agent: Googlebot
Allow: /
Disallow: /private/
User-Agent: Applebot
Disallow: /
User-Agent: Bingbot
Disallow: /
Sitemap: https://acme.com/sitemap.xml
```
### Robots object
```tsx
type Robots = {
rules:
| {
userAgent?: string | string[]
allow?: string | string[]
disallow?: string | string[]
crawlDelay?: number
}
| Array<{
userAgent: string | string[]
allow?: string | string[]
disallow?: string | string[]
crawlDelay?: number
}>
sitemap?: string | string[]
host?: string
}
```
## Version History
| Version | Changes |
| --------- | -------------------- |
| `v13.3.0` | `robots` introduced. |
@@ -0,0 +1,426 @@
---
title: sitemap.xml
description: API Reference for the sitemap.xml file.
related:
title: Next Steps
description: Learn how to use the generateSitemaps function.
links:
- app/api-reference/functions/generate-sitemaps
---
`sitemap.(xml|js|ts)` is a special file that matches the [Sitemaps XML format](https://www.sitemaps.org/protocol.html) to help search engine crawlers index your site more efficiently.
### Sitemap files (.xml)
For smaller applications, you can create a `sitemap.xml` file and place it in the root of your `app` directory.
```xml filename="app/sitemap.xml"
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://acme.com</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>yearly</changefreq>
<priority>1</priority>
</url>
<url>
<loc>https://acme.com/about</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://acme.com/blog</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
```
### Generating a sitemap using code (.js, .ts)
You can use the `sitemap.(js|ts)` file convention to programmatically **generate** a sitemap by exporting a default function that returns an array of URLs. If using TypeScript, a [`Sitemap`](#returns) type is available.
> **Good to know**: `sitemap.js` is a special Route Handler that is cached by default unless it uses a [Request-time API](/docs/app/glossary#request-time-apis) or [dynamic config](/docs/app/guides/caching-without-cache-components#dynamic) option.
```ts filename="app/sitemap.ts" switcher
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
{
url: 'https://acme.com/about',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: 'https://acme.com/blog',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.5,
},
]
}
```
```js filename="app/sitemap.js" switcher
export default function sitemap() {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
{
url: 'https://acme.com/about',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: 'https://acme.com/blog',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.5,
},
]
}
```
Output:
```xml filename="acme.com/sitemap.xml"
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://acme.com</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>yearly</changefreq>
<priority>1</priority>
</url>
<url>
<loc>https://acme.com/about</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://acme.com/blog</loc>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
```
### Image Sitemaps
You can use `images` property to create image sitemaps. Learn more details in the [Google Developer Docs](https://developers.google.com/search/docs/crawling-indexing/sitemaps/image-sitemaps).
```ts filename="app/sitemap.ts" switcher
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: '2021-01-01',
changeFrequency: 'weekly',
priority: 0.5,
images: ['https://example.com/image.jpg'],
},
]
}
```
Output:
```xml filename="acme.com/sitemap.xml"
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"
>
<url>
<loc>https://example.com</loc>
<image:image>
<image:loc>https://example.com/image.jpg</image:loc>
</image:image>
<lastmod>2021-01-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
```
### Video Sitemaps
You can use `videos` property to create video sitemaps. Learn more details in the [Google Developer Docs](https://developers.google.com/search/docs/crawling-indexing/sitemaps/video-sitemaps).
```ts filename="app/sitemap.ts" switcher
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://example.com',
lastModified: '2021-01-01',
changeFrequency: 'weekly',
priority: 0.5,
videos: [
{
title: 'example',
thumbnail_loc: 'https://example.com/image.jpg',
description: 'this is the description',
},
],
},
]
}
```
Output:
```xml filename="acme.com/sitemap.xml"
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
>
<url>
<loc>https://example.com</loc>
<video:video>
<video:title>example</video:title>
<video:thumbnail_loc>https://example.com/image.jpg</video:thumbnail_loc>
<video:description>this is the description</video:description>
</video:video>
<lastmod>2021-01-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>
```
### Generate a localized Sitemap
```ts filename="app/sitemap.ts" switcher
import type { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es',
de: 'https://acme.com/de',
},
},
},
{
url: 'https://acme.com/about',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es/about',
de: 'https://acme.com/de/about',
},
},
},
{
url: 'https://acme.com/blog',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es/blog',
de: 'https://acme.com/de/blog',
},
},
},
]
}
```
```js filename="app/sitemap.js" switcher
export default function sitemap() {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es',
de: 'https://acme.com/de',
},
},
},
{
url: 'https://acme.com/about',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es/about',
de: 'https://acme.com/de/about',
},
},
},
{
url: 'https://acme.com/blog',
lastModified: new Date(),
alternates: {
languages: {
es: 'https://acme.com/es/blog',
de: 'https://acme.com/de/blog',
},
},
},
]
}
```
Output:
```xml filename="acme.com/sitemap.xml"
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://acme.com</loc>
<xhtml:link
rel="alternate"
hreflang="es"
href="https://acme.com/es"/>
<xhtml:link
rel="alternate"
hreflang="de"
href="https://acme.com/de"/>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
</url>
<url>
<loc>https://acme.com/about</loc>
<xhtml:link
rel="alternate"
hreflang="es"
href="https://acme.com/es/about"/>
<xhtml:link
rel="alternate"
hreflang="de"
href="https://acme.com/de/about"/>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
</url>
<url>
<loc>https://acme.com/blog</loc>
<xhtml:link
rel="alternate"
hreflang="es"
href="https://acme.com/es/blog"/>
<xhtml:link
rel="alternate"
hreflang="de"
href="https://acme.com/de/blog"/>
<lastmod>2023-04-06T15:02:24.021Z</lastmod>
</url>
</urlset>
```
### Generating multiple sitemaps
While a single sitemap will work for most applications. For large web applications, you may need to split a sitemap into multiple files.
There are two ways you can create multiple sitemaps:
- By nesting `sitemap.(xml|js|ts)` inside multiple route segments e.g. `app/sitemap.xml` and `app/products/sitemap.xml`.
- By using the [`generateSitemaps`](/docs/app/api-reference/functions/generate-sitemaps) function.
For example, to split a sitemap using `generateSitemaps`, return an array of objects with the sitemap `id`. Then, use the `id` to generate the unique sitemaps.
```ts filename="app/product/sitemap.ts" switcher
import type { MetadataRoute } from 'next'
import { BASE_URL } from '@/app/lib/constants'
export async function generateSitemaps() {
// Fetch the total number of products and calculate the number of sitemaps needed
return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
}
export default async function sitemap(props: {
id: Promise<string>
}): Promise<MetadataRoute.Sitemap> {
const id = await props.id
// Google's limit is 50,000 URLs per sitemap
const start = id * 50000
const end = start + 50000
const products = await getProducts(
`SELECT id, date FROM products WHERE id BETWEEN ${start} AND ${end}`
)
return products.map((product) => ({
url: `${BASE_URL}/product/${product.id}`,
lastModified: product.date,
}))
}
```
```js filename="app/product/sitemap.js" switcher
import { BASE_URL } from '@/app/lib/constants'
export async function generateSitemaps() {
// Fetch the total number of products and calculate the number of sitemaps needed
return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]
}
export default async function sitemap(props) {
const id = await props.id
// Google's limit is 50,000 URLs per sitemap
const start = id * 50000
const end = start + 50000
const products = await getProducts(
`SELECT id, date FROM products WHERE id BETWEEN ${start} AND ${end}`
)
return products.map((product) => ({
url: `${BASE_URL}/product/${product.id}`,
lastModified: product.date,
}))
}
```
Your generated sitemaps will be available at `/.../sitemap/[id]`. For example, `/product/sitemap/1.xml`.
See the [`generateSitemaps` API reference](/docs/app/api-reference/functions/generate-sitemaps) for more information.
## Returns
The default function exported from `sitemap.(xml|ts|js)` should return an array of objects with the following properties:
```tsx
type Sitemap = Array<{
url: string
lastModified?: string | Date
changeFrequency?:
| 'always'
| 'hourly'
| 'daily'
| 'weekly'
| 'monthly'
| 'yearly'
| 'never'
priority?: number
alternates?: {
languages?: Languages<string>
}
}>
```
## Version History
| Version | Changes |
| ---------- | ------------------------------------------------------------ |
| `v16.0.0` | `id` is now a promise that resolves to a `string`. |
| `v14.2.0` | Add localizations support. |
| `v13.4.14` | Add `changeFrequency` and `priority` attributes to sitemaps. |
| `v13.3.0` | `sitemap` introduced. |
@@ -0,0 +1,22 @@
---
title: dynamicParams
description: API reference for the dynamicParams route segment config option.
---
The `dynamicParams` option allows you to control what happens when a dynamic segment is visited that was not generated with [generateStaticParams](/docs/app/api-reference/functions/generate-static-params).
```tsx filename="layout.tsx | page.tsx" switcher
export const dynamicParams = true // true | false
```
```js filename="layout.js | page.js | route.js" switcher
export const dynamicParams = true // true | false
```
- **`true`** (default): Dynamic route segments not included in `generateStaticParams` are generated at request time.
- **`false`**: Dynamic route segments not included in `generateStaticParams` will return a 404.
> **Good to know**:
>
> - This option replaces the `fallback: true | false | blocking` option of `getStaticPaths` in the `pages` directory.
> - `dynamicParams` is not available when [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled.
@@ -0,0 +1,21 @@
---
title: Route Segment Config
description: Learn about how to configure options for Next.js route segments.
---
The Route Segment Config options allow you to configure the behavior of a [Page](/docs/app/api-reference/file-conventions/page), [Layout](/docs/app/api-reference/file-conventions/layout), or [Route Handler](/docs/app/api-reference/file-conventions/route) by directly exporting the following variables:
| Option | Type | Default |
| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------- |
| [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config/dynamicParams) | `boolean` | `true` |
| [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config/runtime) | `'nodejs' \| 'edge'` | `'nodejs'` |
| [`preferredRegion`](/docs/app/api-reference/file-conventions/route-segment-config/preferredRegion) | `'auto' \| 'global' \| 'home' \| string \| string[]` | `'auto'` |
| [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config/maxDuration) | `number` | Set by deployment platform |
## Version History
| Version | |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v16.0.0` | `dynamic`, `dynamicParams`, `revalidate`, and `fetchCache` removed when [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled. See [Caching and Revalidating (Previous Model)](/docs/app/guides/caching-without-cache-components#route-segment-config). |
| `v16.0.0` | `export const experimental_ppr = true` removed. A [codemod](/docs/app/guides/upgrading/codemods#remove-experimental_ppr-route-segment-config-from-app-router-pages-and-layouts) is available. |
| `v15.0.0-RC` | `export const runtime = "experimental-edge"` deprecated. A [codemod](/docs/app/guides/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge) is available. |
@@ -0,0 +1,138 @@
---
title: instant
description: API reference for the instant route segment config.
version: draft
related:
title: Next Steps
description: Learn how to use instant navigations in practice.
links:
- app/guides/instant-navigation
- app/getting-started/caching
- app/getting-started/revalidating
- app/api-reference/directives/use-cache
---
The `unstable_instant` route segment config opts a route into validation for instant client-side navigations. Next.js checks, during development and at build time, that the caching structure produces an instant [static shell](/docs/app/glossary#static-shell) at every possible entry point into the route.
> **Good to know**:
>
> - The `unstable_instant` export only works when [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled.
> - `unstable_instant` cannot be used in Client Components. It will throw an error.
```tsx filename="layout.tsx | page.tsx" switcher
export const unstable_instant = {
prefetch: 'static',
}
export default function Page() {
return <div>...</div>
}
```
```jsx filename="layout.js | page.js" switcher
export const unstable_instant = {
prefetch: 'static',
}
export default function Page() {
return <div>...</div>
}
```
## Reference
### `prefetch`
Controls the validation and prefetching mode.
```tsx filename="page.tsx"
export const unstable_instant = {
prefetch: 'static',
}
```
- **`'static'`**: Enables validation. Prefetching behavior stays the same (static by default). Components that read cookies or headers are treated as dynamic and must be behind Suspense.
### Disabling instant
Set `false` to exempt a segment from validation:
```tsx filename="app/dashboard/layout.tsx"
export const unstable_instant = false
```
## How validation works
`unstable_instant` triggers validation at every shared layout boundary in the route. Validation runs during development (on page loads and HMR updates) and at build time. Errors appear in the dev error overlay or fail the build.
Each error identifies the component that would block navigation. The fix is usually to cache the data with `use cache` or wrap it in a `<Suspense>` boundary.
## Inspecting loading states
Enable the DevTools toggle with the experimental flag:
```js filename="next.config.js"
module.exports = {
experimental: {
instantNavigationDevToolsToggle: true,
},
}
```
Open the Next.js DevTools and select **Instant Navs**. Two options are available:
- **Page load**: click **Reload** to refresh the page and freeze it at the initial static UI that was generated for this route, before any dynamic data streams in.
- **Client navigation**: once enabled, clicking any link in your app shows the prefetched UI for that page instead of the full result.
Use both to check that your loading states look right on first visit and on navigation.
## Testing instant navigation
The `@next/playwright` package exports an `instant()` helper that holds back dynamic content while the callback runs against the static shell. See the [guide](/docs/app/guides/instant-navigation#prevent-regressions-with-e2e-tests) for a full example.
```typescript
import { instant } from '@next/playwright'
```
{/* TODO: remove when fixed and from prod-docs-release */}
## Known issue: shared cookie across projects
The DevTools use a `next-instant-navigation-testing` cookie to freeze the UI at the static shell. Because cookies are scoped to the domain and not the port, running multiple projects on the same domain (typically `localhost`) means the cookie is shared across them and can cause unexpected behavior. Clear the cookie or close the Instant Navs panel when switching between projects to avoid issues.
> **Good to know:** This will be fixed as part of stabilizing the feature.
## TypeScript
```tsx
type RuntimeSample = {
cookies?: Array<{ name: string; value: string }>
headers?: Array<[string, string]>
params?: Record<string, string | string[]>
searchParams?: Record<string, string | string[]>
}
type InstantConfig =
| false
| {
prefetch: 'static'
from?: string[]
unstable_disableValidation?: boolean
}
| {
prefetch: 'runtime'
samples: RuntimeSample[]
from?: string[]
unstable_disableValidation?: boolean
}
export const unstable_instant: InstantConfig = {
prefetch: 'static',
}
```
## Version History
| Version | Changes |
| --------- | ------------------------------------------------------------ |
| `v16.x.x` | `unstable_instant` export introduced (Cache Components only) |
@@ -0,0 +1,24 @@
---
title: maxDuration
description: API reference for the maxDuration route segment config option.
---
The `maxDuration` option allows you to set the maximum execution time (in seconds) for server-side logic in a route segment. Deployment platforms can use `maxDuration` from the Next.js build output to add specific execution limits.
```tsx filename="layout.tsx | page.tsx | route.ts" switcher
export const maxDuration = 5
```
```js filename="layout.js | page.js | route.js" switcher
export const maxDuration = 5
```
## Server Actions
If using [Server Actions](/docs/app/getting-started/mutating-data), set the `maxDuration` at the page level to change the default timeout of all Server Actions used on the page.
## Version History
| Version | Changes |
| ---------- | ------------------------- |
| `v13.4.10` | `maxDuration` introduced. |
@@ -0,0 +1,33 @@
---
title: preferredRegion
description: API reference for the preferredRegion route segment config option.
---
The `preferredRegion` option allows you to specify the preferred deployment region for a route segment. This value is passed to your deployment platform.
```tsx filename="layout.tsx | page.tsx | route.ts" switcher
export const preferredRegion = // string || string[]
```
```js filename="layout.js | page.js | route.js" switcher
export const preferredRegion = // string || string[]
```
- **`string`**: Deploy the route to a specific region. Available region codes are platform-specific. For example, `'iad1'`.
- **`string[]`**: Deploy the route to multiple specific regions. The route is deployed to **all** listed regions, not a single one chosen from the list. For example, `['iad1', 'sfo1']`.
> **Good to know**:
>
> - If a `preferredRegion` is not specified, it will inherit the option of the nearest parent layout. The root layout defaults to `'auto'`.
> - A child segment's value overrides the parent, values are not merged.
> - Next.js passes the region values through to the deployment platform. The exact behavior and available region codes are platform-specific. Refer to your deployment platform's documentation for supported values.
## Vercel
If deploying Next.js on Vercel, regions are only supported if `export const runtime = 'edge'` is set. The following options can be passed:
- **`'auto'`** (default): Uses the default region.
- **`'global'`**: Prefer deploying the route to all availableregions.
- **`'home'`**: Prefer deploying the route to the home region.
If an unsupported value is passed, an error will be thrown.
@@ -0,0 +1,24 @@
---
title: runtime
description: API reference for the runtime route segment config option.
---
The `runtime` option allows you to select the JavaScript runtime used for rendering your route.
```tsx filename="layout.tsx | page.tsx | route.ts" switcher
export const runtime = 'nodejs'
// 'nodejs' | 'edge'
```
```js filename="layout.js | page.js | route.js" switcher
export const runtime = 'nodejs'
// 'nodejs' | 'edge'
```
- **`'nodejs'`** (default)
- **`'edge'`**
> **Good to know**:
>
> - Using `runtime: 'edge'` is **not supported** for Cache Components.
> - This option cannot be used in [Proxy](/docs/app/api-reference/file-conventions/proxy).
@@ -0,0 +1,66 @@
---
title: default.js
description: API Reference for the default.js file.
related:
title: Learn more about Parallel Routes
links:
- app/api-reference/file-conventions/parallel-routes
---
The `default.js` file is used to render a fallback within [Parallel Routes](/docs/app/api-reference/file-conventions/parallel-routes) when Next.js cannot recover a [slot's](/docs/app/api-reference/file-conventions/parallel-routes#slots) active state after a full-page load.
During [soft navigation](/docs/app/getting-started/linking-and-navigating#client-side-transitions), Next.js keeps track of the active _state_ (subpage) for each slot. However, for hard navigations (full-page load), Next.js cannot recover the active state. In this case, a `default.js` file can be rendered for subpages that don't match the current URL.
Consider the following folder structure. The `@team` slot has a `settings` page, but `@analytics` does not.
<Image
alt="Parallel Routes unmatched routes"
srcLight="/docs/light/parallel-routes-unmatched-routes.png"
srcDark="/docs/dark/parallel-routes-unmatched-routes.png"
width="1600"
height="930"
/>
When navigating to `/settings`, the `@team` slot will render the `settings` page while maintaining the currently active page for the `@analytics` slot.
On refresh, Next.js will render a `default.js` for `@analytics`. If `default.js` doesn't exist, an error is returned for named slots (`@team`, `@analytics`, etc) and requires you to define a `default.js` in order to continue. If you want to preserve the old behavior of returning a 404 in these situations, you can create a `default.js` that contains:
```tsx filename="app/@team/default.js"
import { notFound } from 'next/navigation'
export default function Default() {
notFound()
}
```
Additionally, since `children` is an implicit slot, you also need to create a `default.js` file to render a fallback for `children` when Next.js cannot recover the active state of the parent page. If you don't create a `default.js` for the `children` slot, it will return a 404 page for the route.
## Reference
### `params` (optional)
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) from the root segment down to the slot's subpages. For example:
```tsx filename="app/[artist]/@sidebar/default.js" switcher
export default async function Default({
params,
}: {
params: Promise<{ artist: string }>
}) {
const { artist } = await params
}
```
```jsx filename="app/[artist]/@sidebar/default.js" switcher
export default async function Default({ params }) {
const { artist } = await params
}
```
| Example | URL | `params` |
| ------------------------------------------ | ------------ | -------------------------------------------- |
| `app/[artist]/@sidebar/default.js` | `/zack` | `Promise<{ artist: 'zack' }>` |
| `app/[artist]/[album]/@sidebar/default.js` | `/zack/next` | `Promise<{ artist: 'zack', album: 'next' }>` |
- Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
@@ -0,0 +1,375 @@
---
title: Dynamic Route Segments
nav_title: Dynamic Segments
description: Dynamic Route Segments can be used to programmatically generate route segments from dynamic data.
related:
title: Next Steps
description: For more information on what to do next, we recommend the following sections
links:
- app/api-reference/functions/generate-static-params
---
When you don't know the exact route segment names ahead of time and want to create routes from dynamic data, you can use Dynamic Segments that are filled in at request time or prerendered at build time.
## Convention
A Dynamic Segment can be created by wrapping a folder's name in square brackets: `[folderName]`. For example, a blog could include the following route `app/blog/[slug]/page.js` where `[slug]` is the Dynamic Segment for blog posts.
```tsx filename="app/blog/[slug]/page.tsx" switcher
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <div>My Post: {slug}</div>
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
export default async function Page({ params }) {
const { slug } = await params
return <div>My Post: {slug}</div>
}
```
Dynamic Segments are passed as the `params` prop to [`layout`](/docs/app/api-reference/file-conventions/layout), [`page`](/docs/app/api-reference/file-conventions/page), [`route`](/docs/app/api-reference/file-conventions/route), and [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) functions.
| Route | Example URL | `params` |
| ------------------------- | ----------- | --------------- |
| `app/blog/[slug]/page.js` | `/blog/a` | `{ slug: 'a' }` |
| `app/blog/[slug]/page.js` | `/blog/b` | `{ slug: 'b' }` |
| `app/blog/[slug]/page.js` | `/blog/c` | `{ slug: 'c' }` |
### In Client Components
In a Client Component **page**, dynamic segments from props can be accessed using the [`use`](https://react.dev/reference/react/use) API.
```tsx filename="app/blog/[slug]/page.tsx" switcher
'use client'
import { use } from 'react'
export default function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
return (
<div>
<p>{slug}</p>
</div>
)
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
'use client'
import { use } from 'react'
export default function BlogPostPage({ params }) {
const { slug } = use(params)
return (
<div>
<p>{slug}</p>
</div>
)
}
```
Alternatively Client Components can use the [`useParams`](/docs/app/api-reference/functions/use-params) hook to access the `params` anywhere in the Client Component tree.
### Catch-all Segments
Dynamic Segments can be extended to **catch-all** subsequent segments by adding an ellipsis inside the brackets `[...folderName]`.
For example, `app/shop/[...slug]/page.js` will match `/shop/clothes`, but also `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`, and so on.
| Route | Example URL | `params` |
| ---------------------------- | ------------- | --------------------------- |
| `app/shop/[...slug]/page.js` | `/shop/a` | `{ slug: ['a'] }` |
| `app/shop/[...slug]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` |
| `app/shop/[...slug]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |
### Optional Catch-all Segments
Catch-all Segments can be made **optional** by including the parameter in double square brackets: `[[...folderName]]`.
For example, `app/shop/[[...slug]]/page.js` will **also** match `/shop`, in addition to `/shop/clothes`, `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`.
The difference between **catch-all** and **optional catch-all** segments is that with optional, the route without the parameter is also matched (`/shop` in the example above).
| Route | Example URL | `params` |
| ------------------------------ | ------------- | --------------------------- |
| `app/shop/[[...slug]]/page.js` | `/shop` | `{ slug: undefined }` |
| `app/shop/[[...slug]]/page.js` | `/shop/a` | `{ slug: ['a'] }` |
| `app/shop/[[...slug]]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` |
| `app/shop/[[...slug]]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` |
### TypeScript
When using TypeScript, you can add types for `params` depending on your configured route segment — use [`PageProps<'/route'>`](/docs/app/api-reference/file-conventions/page#page-props-helper), [`LayoutProps<'/route'>`](/docs/app/api-reference/file-conventions/layout#layout-props-helper), or [`RouteContext<'/route'>`](/docs/app/api-reference/file-conventions/route#route-context-helper) to type `params` in `page`, `layout`, and `route` respectively.
Route `params` values are typed as `string`, `string[]`, or `undefined` (for optional catch-all segments), because their values aren't known until runtime. Users can enter any URL into the address bar, and these broad types help ensure that your application code handles all these possible cases.
| Route | `params` Type Definition |
| ----------------------------------- | ---------------------------------------- |
| `app/blog/[slug]/page.js` | `{ slug: string }` |
| `app/shop/[...slug]/page.js` | `{ slug: string[] }` |
| `app/shop/[[...slug]]/page.js` | `{ slug?: string[] }` |
| `app/[categoryId]/[itemId]/page.js` | `{ categoryId: string, itemId: string }` |
If you're working on a route where `params` can only have a fixed number of valid values, such as a `[locale]` param with a known set of language codes, you can use runtime validation to handle any invalid params a user may enter, and let the rest of your application work with the narrower type from your known set.
```tsx filename="/app/[locale]/page.tsx"
import { notFound } from 'next/navigation'
import type { Locale } from '@i18n/types'
import { isValidLocale } from '@i18n/utils'
function assertValidLocale(value: string): asserts value is Locale {
if (!isValidLocale(value)) notFound()
}
export default async function Page(props: PageProps<'/[locale]'>) {
const { locale } = await props.params // locale is typed as string
assertValidLocale(locale)
// locale is now typed as Locale
}
```
## Behavior
- Since the `params` prop is a promise. You must use `async`/`await` or React's use function to access the values.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
### With Cache Components
When using [Cache Components](/docs/app/getting-started/caching) with dynamic route segments, how you handle params depends on whether you use [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params).
Without `generateStaticParams`, param values are unknown during prerendering, making params runtime data. You must wrap param access in `<Suspense>` boundaries to provide fallback UI.
With `generateStaticParams`, you provide sample param values that can be used at build time. The build process validates that dynamic content and other runtime APIs are correctly handled, then generates static HTML files for the samples. Pages rendered with runtime params are saved to disk after a successful first request.
The sections below demonstrate both patterns.
#### Without `generateStaticParams`
All params are runtime data. Param access must be wrapped by Suspense fallback UI. Next.js generates a static shell at build time, and content loads on each request.
> **Good to know**: You can also use [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) for page-level fallback UI.
```tsx filename="app/blog/[slug]/page.tsx"
import { Suspense } from 'react'
export default function Page({ params }: PageProps<'/blog/[slug]'>) {
return (
<div>
<h1>Blog Post</h1>
<Suspense fallback={<div>Loading...</div>}>
{params.then(({ slug }) => (
<Content slug={slug} />
))}
</Suspense>
</div>
)
}
async function Content({ slug }: { slug: string }) {
const res = await fetch(`https://api.vercel.app/blog/${slug}`)
const post = await res.json()
return (
<article>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
)
}
```
#### With `generateStaticParams`
Provide params ahead of time to prerender pages at build time. You can prerender all routes or a subset depending on your needs.
During the build process, the route is executed with each sample param to collect the HTML result. If dynamic content or runtime data are accessed incorrectly, the build will fail.
```tsx filename="app/blog/[slug]/page.tsx" highlight={3-5,8,19}
import { Suspense } from 'react'
export async function generateStaticParams() {
return [{ slug: '1' }, { slug: '2' }, { slug: '3' }]
}
export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params
return (
<div>
<h1>Blog Post</h1>
<Content slug={slug} />
</div>
)
}
async function Content({ slug }: { slug: string }) {
const post = await getPost(slug)
return (
<article>
<h2>{post.title}</h2>
<p>{post.content}</p>
</article>
)
}
async function getPost(slug: string) {
'use cache'
const res = await fetch(`https://api.vercel.app/blog/${slug}`)
return res.json()
}
```
Build-time validation only covers code paths that execute with the sample params. If your route has conditional logic that accesses runtime APIs for certain param values not in your samples, those branches won't be validated at build time:
```tsx filename="app/blog/[slug]/page.tsx"
import { cookies } from 'next/headers'
export async function generateStaticParams() {
return [{ slug: 'public-post' }, { slug: 'hello-world' }]
}
export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params
if (slug.startsWith('private-')) {
// This branch is never executed at build time
// Runtime requests for 'private-*' slugs will error
return <PrivatePost slug={slug} />
}
return <PublicPost slug={slug} />
}
async function PrivatePost({ slug }: { slug: string }) {
const token = (await cookies()).get('token')
// ... fetch and render private post using token for auth
}
```
For runtime params not returned by `generateStaticParams`, validation occurs during the first request. In the example above, requests for slugs starting with `private-` will fail because `PrivatePost` accesses `cookies()` without a Suspense boundary. Other runtime params that don't hit the conditional branch will render successfully and be saved to disk for subsequent requests.
To fix this, wrap `PrivatePost` with Suspense:
```tsx filename="app/blog/[slug]/page.tsx" highlight={13-15}
import { Suspense } from 'react'
import { cookies } from 'next/headers'
export async function generateStaticParams() {
return [{ slug: 'public-post' }, { slug: 'hello-world' }]
}
export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
const { slug } = await params
if (slug.startsWith('private-')) {
return (
<Suspense fallback={<div>Loading...</div>}>
<PrivatePost slug={slug} />
</Suspense>
)
}
return <PublicPost slug={slug} />
}
async function PrivatePost({ slug }: { slug: string }) {
const token = (await cookies()).get('token')
// ... fetch and render private post using token for auth
}
```
## Examples
### With `generateStaticParams`
The [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) function can be used to [statically generate](/docs/app/glossary#prerendering) routes at build time instead of on-demand at request time.
```tsx filename="app/blog/[slug]/page.tsx" switcher
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
```
When using `fetch` inside the `generateStaticParams` function, the requests are [automatically deduplicated](/docs/app/glossary#memoization). This avoids multiple network calls for the same data Layouts, Pages, and other `generateStaticParams` functions, speeding up build time.
### Dynamic GET Route Handlers with `generateStaticParams`
`generateStaticParams` also works with dynamic [Route Handlers](/docs/app/api-reference/file-conventions/route) to statically generate API responses at build time:
```ts filename="app/api/posts/[id]/route.ts" switcher
export async function generateStaticParams() {
const posts: { id: number }[] = await fetch(
'https://api.vercel.app/blog'
).then((res) => res.json())
return posts.map((post) => ({
id: `${post.id}`,
}))
}
export async function GET(
request: Request,
{ params }: RouteContext<'/api/posts/[id]'>
) {
const { id } = await params
const res = await fetch(`https://api.vercel.app/blog/${id}`)
if (!res.ok) {
return Response.json({ error: 'Post not found' }, { status: 404 })
}
const post = await res.json()
return Response.json(post)
}
```
```js filename="app/api/posts/[id]/route.js" switcher
export async function generateStaticParams() {
const posts = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: `${post.id}`,
}))
}
export async function GET(request, { params }) {
const { id } = await params
const res = await fetch(`https://api.vercel.app/blog/${id}`)
if (!res.ok) {
return Response.json({ error: 'Post not found' }, { status: 404 })
}
const post = await res.json()
return Response.json(post)
}
```
In this example, route handlers for all blog post IDs returned by `generateStaticParams` will be statically generated at build time. Requests to other IDs will be handled dynamically at request time.
@@ -0,0 +1,332 @@
---
title: error.js
description: API reference for the error.js special file.
related:
title: Learn more about error handling
links:
- app/getting-started/error-handling
---
An **error** file allows you to handle unexpected runtime errors and display fallback UI.
<Image
alt="error.js special file"
srcLight="/docs/light/error-special-file.png"
srcDark="/docs/dark/error-special-file.png"
width="1600"
height="606"
/>
```tsx filename="app/dashboard/error.tsx" switcher
'use client' // Error boundaries must be Client Components
import { useEffect } from 'react'
export default function Error({
error,
unstable_retry,
}: {
error: Error & { digest?: string }
unstable_retry: () => void
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by re-fetching and re-rendering the segment
() => unstable_retry()
}
>
Try again
</button>
</div>
)
}
```
```jsx filename="app/dashboard/error.js" switcher
'use client' // Error boundaries must be Client Components
import { useEffect } from 'react'
export default function Error({ error, unstable_retry }) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button
onClick={
// Attempt to recover by re-fetching and re-rendering the segment
() => unstable_retry()
}
>
Try again
</button>
</div>
)
}
```
`error.js` wraps a route segment and its nested children in a [React Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary). When an error throws within the boundary, the `error` component shows as the fallback UI.
<Image
alt="How error.js works"
srcLight="/docs/light/error-overview.png"
srcDark="/docs/dark/error-overview.png"
width="1600"
height="903"
/>
> **Good to know**:
>
> - The [React DevTools](https://react.dev/learn/react-developer-tools) allow you to toggle error boundaries to test error states.
> - If you want errors to bubble up to the parent error boundary, you can `throw` when rendering the `error` component.
> - For component-level error recovery that aren't tied to route segments like [`error.js`](/docs/app/api-reference/file-conventions/error), use the [`unstable_catchError`](/docs/app/api-reference/functions/catchError) function.
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `error.js` wraps `loading.js`, `not-found.js`, `page.js`, and nested `layout.js` files in a React error boundary. It does **not** wrap the `layout.js` or `template.js` above it in the same segment. To handle errors in the root layout, use [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error).
## Reference
### Props
#### `error`
An instance of an [`Error`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error) object forwarded to the `error.js` Client Component.
> **Good to know:** During development, the `Error` object forwarded to the client will be serialized and include the `message` of the original error for easier debugging. However, **this behavior is different in production** to avoid leaking potentially sensitive details included in the error to the client.
#### `error.message`
- Errors forwarded from Client Components show the original `Error` message.
- Errors forwarded from Server Components show a generic message with an identifier. This is to prevent leaking sensitive details. You can use the identifier, under `errors.digest`, to match the corresponding server-side logs.
#### `error.digest`
An automatically generated hash of the error thrown. It can be used to match the corresponding error in server-side logs.
#### `unstable_retry`
The cause of an error can sometimes be temporary. In these cases, trying again might resolve the issue.
An error component can use the `unstable_retry()` function to prompt the user to attempt to recover from the error. When executed, the function will try to re-fetch and re-render the error boundary's children. If successful, the fallback error component is replaced with the result of the re-render.
```tsx filename="app/dashboard/error.tsx" switcher
'use client' // Error boundaries must be Client Components
export default function Error({
error,
unstable_retry,
}: {
error: Error & { digest?: string }
unstable_retry: () => void
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => unstable_retry()}>Try again</button>
</div>
)
}
```
```jsx filename="app/dashboard/error.js" switcher
'use client' // Error boundaries must be Client Components
export default function Error({ error, unstable_retry }) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => unstable_retry()}>Try again</button>
</div>
)
}
```
#### `reset`
In most cases, you should use [`unstable_retry()`](#unstable_retry) instead. However, if you have a specific reason to clear the error state and re-render the error boundary's children without re-fetching the contents, you can use the `reset()` function.
## Examples
### Global Error
While less common, you can handle errors in the root layout or template using `global-error.jsx`, located in the root app directory, even when leveraging [internationalization](/docs/app/guides/internationalization). Global error UI must define its own `<html>` and `<body>` tags, global styles, fonts, or other dependencies that your error page requires. This file replaces the root layout or template when active.
> **Good to know**: Error boundaries must be [Client Components](/docs/app/getting-started/server-and-client-components#using-client-components), which means that [`metadata` and `generateMetadata`](/docs/app/getting-started/metadata-and-og-images) exports are not supported in `global-error.jsx`. As an alternative, you can use the React [`<title>`](https://react.dev/reference/react-dom/components/title) component.
```tsx filename="app/global-error.tsx" switcher
'use client' // Error boundaries must be Client Components
export default function GlobalError({
error,
unstable_retry,
}: {
error: Error & { digest?: string }
unstable_retry: () => void
}) {
return (
// global-error must include html and body tags
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => unstable_retry()}>Try again</button>
</body>
</html>
)
}
```
```jsx filename="app/global-error.js" switcher
'use client' // Error boundaries must be Client Components
export default function GlobalError({ error, unstable_retry }) {
return (
// global-error must include html and body tags
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={() => unstable_retry()}>Try again</button>
</body>
</html>
)
}
```
### Graceful error recovery with a custom error boundary
When rendering fails on the client, it can be useful to show the last known server rendered UI for a better user experience.
The `GracefullyDegradingErrorBoundary` is an example of a custom error boundary that captures and preserves the current HTML before an error occurs. If a rendering error happens, it re-renders the captured HTML and displays a persistent notification bar to inform the user.
```tsx filename="app/dashboard/error.tsx" switcher
'use client'
import React, { Component, ErrorInfo, ReactNode } from 'react'
interface ErrorBoundaryProps {
children: ReactNode
onError?: (error: Error, errorInfo: ErrorInfo) => void
}
interface ErrorBoundaryState {
hasError: boolean
}
export class GracefullyDegradingErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
private contentRef: React.RefObject<HTMLDivElement | null>
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false }
this.contentRef = React.createRef()
}
static getDerivedStateFromError(_: Error): ErrorBoundaryState {
return { hasError: true }
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
if (this.props.onError) {
this.props.onError(error, errorInfo)
}
}
render() {
if (this.state.hasError) {
// Render the current HTML content without hydration
return (
<>
<div
ref={this.contentRef}
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: this.contentRef.current?.innerHTML || '',
}}
/>
<div className="fixed bottom-0 left-0 right-0 bg-red-600 text-white py-4 px-6 text-center">
<p className="font-semibold">
An error occurred during page rendering
</p>
</div>
</>
)
}
return <div ref={this.contentRef}>{this.props.children}</div>
}
}
export default GracefullyDegradingErrorBoundary
```
```jsx filename="app/dashboard/error.js" switcher
'use client'
import React, { Component, createRef } from 'react'
class GracefullyDegradingErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = { hasError: false }
this.contentRef = createRef()
}
static getDerivedStateFromError(_) {
return { hasError: true }
}
componentDidCatch(error, errorInfo) {
if (this.props.onError) {
this.props.onError(error, errorInfo)
}
}
render() {
if (this.state.hasError) {
// Render the current HTML content without hydration
return (
<>
<div
ref={this.contentRef}
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: this.contentRef.current?.innerHTML || '',
}}
/>
<div className="fixed bottom-0 left-0 right-0 bg-red-600 text-white py-4 px-6 text-center">
<p className="font-semibold">
An error occurred during page rendering
</p>
</div>
</>
)
}
return <div ref={this.contentRef}>{this.props.children}</div>
}
}
export default GracefullyDegradingErrorBoundary
```
## Version History
| Version | Changes |
| --------- | ------------------------------------------- |
| `v16.2.0` | `unstable_retry` prop added. |
| `v15.2.0` | Also display `global-error` in development. |
| `v13.1.0` | `global-error` introduced. |
| `v13.0.0` | `error` introduced. |
@@ -0,0 +1,50 @@
---
title: forbidden.js
description: API reference for the forbidden.js special file.
related:
links:
- app/api-reference/functions/forbidden
version: experimental
---
The **forbidden** file is used to render UI when the [`forbidden`](/docs/app/api-reference/functions/forbidden) function is invoked during authentication. Along with allowing you to customize the UI, Next.js will return a `403` status code.
```tsx filename="app/forbidden.tsx" switcher
import Link from 'next/link'
export default function Forbidden() {
return (
<div>
<h2>Forbidden</h2>
<p>You are not authorized to access this resource.</p>
<Link href="/">Return Home</Link>
</div>
)
}
```
```jsx filename="app/forbidden.jsx" switcher
import Link from 'next/link'
export default function Forbidden() {
return (
<div>
<h2>Forbidden</h2>
<p>You are not authorized to access this resource.</p>
<Link href="/">Return Home</Link>
</div>
)
}
```
## Reference
### Props
`forbidden.js` components do not accept any props.
## Version History
| Version | Changes |
| --------- | -------------------------- |
| `v15.1.0` | `forbidden.js` introduced. |
@@ -0,0 +1,4 @@
---
title: File-system conventions
description: API Reference for Next.js file-system conventions.
---
@@ -0,0 +1,224 @@
---
title: instrumentation-client.js
description: Learn how to add client-side instrumentation to track and monitor your Next.js application's frontend performance.
---
The `instrumentation-client.js|ts` file allows you to add monitoring, analytics code, and other side-effects that run before your application becomes interactive. This is useful for setting up performance tracking, error monitoring, polyfills, or any other client-side observability tools.
To use it, place the file in the **root** of your application or inside a `src` folder.
## Usage
Unlike [server-side instrumentation](/docs/app/guides/instrumentation), you do not need to export any specific functions. You can write your monitoring code directly in the file:
```ts filename="instrumentation-client.ts" switcher
// Set up performance monitoring
performance.mark('app-init')
// Initialize analytics
console.log('Analytics initialized')
// Set up error tracking
window.addEventListener('error', (event) => {
// Send to your error tracking service
reportError(event.error)
})
```
```js filename="instrumentation-client.js" switcher
// Set up performance monitoring
performance.mark('app-init')
// Initialize analytics
console.log('Analytics initialized')
// Set up error tracking
window.addEventListener('error', (event) => {
// Send to your error tracking service
reportError(event.error)
})
```
**Error handling:** Implement try-catch blocks around your instrumentation code to ensure robust monitoring. This prevents individual tracking failures from affecting other instrumentation features.
## Router navigation tracking
You can export an `onRouterTransitionStart` function to receive notifications when navigation begins:
```ts filename="instrumentation-client.ts" switcher
performance.mark('app-init')
export function onRouterTransitionStart(
url: string,
navigationType: 'push' | 'replace' | 'traverse'
) {
console.log(`Navigation started: ${navigationType} to ${url}`)
performance.mark(`nav-start-${Date.now()}`)
}
```
```js filename="instrumentation-client.js" switcher
performance.mark('app-init')
export function onRouterTransitionStart(url, navigationType) {
console.log(`Navigation started: ${navigationType} to ${url}`)
performance.mark(`nav-start-${Date.now()}`)
}
```
The `onRouterTransitionStart` function receives two parameters:
- `url: string` - The URL being navigated to
- `navigationType: 'push' | 'replace' | 'traverse'` - The type of navigation
## Performance considerations
Keep instrumentation code lightweight.
Next.js monitors initialization time in development and will log warnings if it takes longer than 16ms, which could impact smooth page loading.
## Execution timing
The `instrumentation-client.js` file executes at a specific point in the application lifecycle:
1. **After** the HTML document is loaded
2. **Before** React hydration begins
3. **Before** user interactions are possible
This timing makes it ideal for setting up error tracking, analytics, and performance monitoring that needs to capture early application lifecycle events.
## Examples
### Error tracking
Initialize error tracking before React starts and add navigation breadcrumbs for better debugging context.
```ts filename="instrumentation-client.ts" switcher
import Monitor from './lib/monitoring'
Monitor.initialize()
export function onRouterTransitionStart(url: string) {
Monitor.pushEvent({
message: `Navigation to ${url}`,
category: 'navigation',
})
}
```
```js filename="instrumentation-client.js" switcher
import Monitor from './lib/monitoring'
Monitor.initialize()
export function onRouterTransitionStart(url) {
Monitor.pushEvent({
message: `Navigation to ${url}`,
category: 'navigation',
})
}
```
### Analytics tracking
Initialize analytics and track navigation events with detailed metadata for user behavior analysis.
```ts filename="instrumentation-client.ts" switcher
import { analytics } from './lib/analytics'
analytics.init()
export function onRouterTransitionStart(url: string, navigationType: string) {
analytics.track('page_navigation', {
url,
type: navigationType,
timestamp: Date.now(),
})
}
```
```js filename="instrumentation-client.js" switcher
import { analytics } from './lib/analytics'
analytics.init()
export function onRouterTransitionStart(url, navigationType) {
analytics.track('page_navigation', {
url,
type: navigationType,
timestamp: Date.now(),
})
}
```
### Performance monitoring
Track Time to Interactive and navigation performance using the Performance Observer API and performance marks.
```ts filename="instrumentation-client.ts" switcher
const startTime = performance.now()
const observer = new PerformanceObserver(
(list: PerformanceObserverEntryList) => {
for (const entry of list.getEntries()) {
if (entry instanceof PerformanceNavigationTiming) {
console.log('Time to Interactive:', entry.loadEventEnd - startTime)
}
}
}
)
observer.observe({ entryTypes: ['navigation'] })
export function onRouterTransitionStart(url: string) {
performance.mark(`nav-start-${url}`)
}
```
```js filename="instrumentation-client.js" switcher
const startTime = performance.now()
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry instanceof PerformanceNavigationTiming) {
console.log('Time to Interactive:', entry.loadEventEnd - startTime)
}
}
})
observer.observe({ entryTypes: ['navigation'] })
export function onRouterTransitionStart(url) {
performance.mark(`nav-start-${url}`)
}
```
### Polyfills
Load polyfills before application code runs. Use static imports for immediate loading and dynamic imports for conditional loading based on feature detection.
```ts filename="instrumentation-client.ts" switcher
import './lib/polyfills'
if (!window.ResizeObserver) {
import('./lib/polyfills/resize-observer').then((mod) => {
window.ResizeObserver = mod.default
})
}
```
```js filename="instrumentation-client.js" switcher
import './lib/polyfills'
if (!window.ResizeObserver) {
import('./lib/polyfills/resize-observer').then((mod) => {
window.ResizeObserver = mod.default
})
}
```
## Version history
| Version | Changes |
| ------- | ----------------------------------- |
| `v15.3` | `instrumentation-client` introduced |
@@ -0,0 +1,139 @@
---
title: instrumentation.js
description: API reference for the instrumentation.js file.
related:
title: Learn more about Instrumentation
links:
- app/guides/instrumentation
---
The `instrumentation.js|ts` file is used to integrate observability tools into your application, allowing you to track the performance and behavior, and to debug issues in production.
To use it, place the file in the **root** of your application or inside a [`src` folder](/docs/app/api-reference/file-conventions/src-folder) if using one.
## Exports
### `register` (optional)
The file exports a `register` function that is called **once** when a new Next.js server instance is initiated, and must complete before the server is ready to handle requests. `register` can be an async function.
```ts filename="instrumentation.ts" switcher
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel('next-app')
}
```
```js filename="instrumentation.js" switcher
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel('next-app')
}
```
### `onRequestError` (optional)
You can optionally export an `onRequestError` function to track **server** errors to any custom observability provider.
- If you're running any async tasks in `onRequestError`, make sure they're awaited. `onRequestError` will be triggered when the Next.js server captures the error.
- The `error` instance might not be the original error instance thrown, as it may be processed by React if encountered during Server Components rendering. If this happens, you can use `digest` property on an error to identify the actual error type.
```ts filename="instrumentation.ts" switcher
import { type Instrumentation } from 'next'
export const onRequestError: Instrumentation.onRequestError = async (
err,
request,
context
) => {
await fetch('https://.../report-error', {
method: 'POST',
body: JSON.stringify({
message: err.message,
request,
context,
}),
headers: {
'Content-Type': 'application/json',
},
})
}
```
```js filename="instrumentation.js" switcher
export async function onRequestError(err, request, context) {
await fetch('https://.../report-error', {
method: 'POST',
body: JSON.stringify({
message: err.message,
request,
context,
}),
headers: {
'Content-Type': 'application/json',
},
})
}
```
#### Parameters
The function accepts three parameters: `error`, `request`, and `context`.
```ts filename="Types"
export function onRequestError(
error: { digest: string } & Error,
request: {
path: string // resource path, e.g. /blog?name=foo
method: string // request method. e.g. GET, POST, etc
headers: { [key: string]: string | string[] }
},
context: {
routerKind: 'Pages Router' | 'App Router' // the router type
routePath: string // the route file path, e.g. /app/blog/[dynamic]
routeType: 'render' | 'route' | 'action' | 'proxy' // the context in which the error occurred
renderSource:
| 'react-server-components'
| 'react-server-components-payload'
| 'server-rendering'
revalidateReason: 'on-demand' | 'stale' | undefined // undefined is a normal request without revalidation
renderType: 'dynamic' | 'dynamic-resume' // 'dynamic-resume' for PPR
}
): void | Promise<void>
```
- `error`: The caught error itself (type is always `Error`), and a `digest` property which is the unique ID of the error.
- `request`: Read-only request information associated with the error.
- `context`: The context in which the error occurred. This can be the type of router (App or Pages Router), and/or (Server Components (`'render'`), Route Handlers (`'route'`), Server Actions (`'action'`), or Proxy (`'proxy'`)).
### Specifying the runtime
The `instrumentation.js` file works in both the Node.js and Edge runtime, however, you can use `process.env.NEXT_RUNTIME` to target a specific runtime.
```js filename="instrumentation.js"
export function register() {
if (process.env.NEXT_RUNTIME === 'edge') {
return require('./register.edge')
} else {
return require('./register.node')
}
}
export function onRequestError() {
if (process.env.NEXT_RUNTIME === 'edge') {
return require('./on-request-error.edge')
} else {
return require('./on-request-error.node')
}
}
```
## Version History
| Version | Changes |
| --------- | ------------------------------------------------------- |
| `v15.0.0` | `onRequestError` introduced, `instrumentation` stable |
| `v14.0.4` | Turbopack support for `instrumentation` |
| `v13.2.0` | `instrumentation` introduced as an experimental feature |
@@ -0,0 +1,83 @@
---
title: Intercepting Routes
description: Use intercepting routes to load a new route within the current layout while masking the browser URL, useful for advanced routing patterns such as modals.
related:
title: Next Steps
description: Learn how to create modals with Intercepted and Parallel Routes.
links:
- app/api-reference/file-conventions/parallel-routes
---
Intercepting routes allows you to load a route from another part of your application within the current layout. This routing paradigm can be useful when you want to display the content of a route without the user switching to a different context.
For example, when clicking on a photo in a feed, you can display the photo in a modal, overlaying the feed. In this case, Next.js intercepts the `/photo/123` route, masks the URL, and overlays it over `/feed`.
<Image
alt="Intercepting routes soft navigation"
srcLight="/docs/light/intercepting-routes-soft-navigate.png"
srcDark="/docs/dark/intercepting-routes-soft-navigate.png"
width="1600"
height="617"
/>
However, when navigating to the photo by clicking a shareable URL or by refreshing the page, the entire photo page should render instead of the modal. No route interception should occur.
<Image
alt="Intercepting routes hard navigation"
srcLight="/docs/light/intercepting-routes-hard-navigate.png"
srcDark="/docs/dark/intercepting-routes-hard-navigate.png"
width="1600"
height="604"
/>
## Convention
Intercepting routes can be defined with the `(..)` convention, which is similar to relative path convention `../` but for route segments.
You can use:
- `(.)` to match segments on the **same level**
- `(..)` to match segments **one level above**
- `(..)(..)` to match segments **two levels above**
- `(...)` to match segments from the **root** `app` directory
For example, you can intercept the `photo` segment from within the `feed` segment by creating a `(..)photo` directory.
<Image
alt="Intercepting routes folder structure"
srcLight="/docs/light/intercepted-routes-files.png"
srcDark="/docs/dark/intercepted-routes-files.png"
width="1600"
height="604"
/>
> **Good to know:** The `(..)` convention is based on _route segments_, not the file-system. For example, it does not consider `@slot` folders in [Parallel Routes](/docs/app/api-reference/file-conventions/parallel-routes).
## Examples
### Modals
Intercepting Routes can be used together with [Parallel Routes](/docs/app/api-reference/file-conventions/parallel-routes) to create modals. This allows you to solve common challenges when building modals, such as:
- Making the modal content **shareable through a URL**.
- **Preserving context** when the page is refreshed, instead of closing the modal.
- **Closing the modal on backwards navigation** rather than going to the previous route.
- **Reopening the modal on forwards navigation**.
Consider the following UI pattern, where a user can open a photo modal from a gallery using client-side navigation, or navigate to the photo page directly from a shareable URL:
<Image
alt="Intercepting routes modal example"
srcLight="/docs/light/intercepted-routes-modal-example.png"
srcDark="/docs/dark/intercepted-routes-modal-example.png"
width="1600"
height="976"
/>
In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a slot and **not** a segment. This means that the `photo` route is only one segment level higher, despite being two file-system levels higher.
See the [Parallel Routes](/docs/app/api-reference/file-conventions/parallel-routes#modals) documentation for a step-by-step example, or see our [image gallery example](https://github.com/vercel-labs/nextgram).
> **Good to know:**
>
> - Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal.
@@ -0,0 +1,731 @@
---
title: layout.js
description: API reference for the layout.js file.
---
The `layout` file is used to define a layout in your Next.js application.
```tsx filename="app/dashboard/layout.tsx" switcher
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
```
```jsx filename="app/dashboard/layout.js" switcher
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
```
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `layout.js` is the outermost component in a route segment. It wraps `template.js`, `error.js`, `loading.js`, `not-found.js`, and `page.js`.
A **root layout** is the top-most layout in the root `app` directory. It is used to define the `<html>` and `<body>` tags and other globally shared UI.
```tsx filename="app/layout.tsx" switcher
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
```
```jsx filename="app/layout.js" switcher
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
```
## Reference
### Props
#### `children` (required)
Layout components should accept and use a `children` prop. During rendering, `children` will be populated with the route segments the layout is wrapping. These will primarily be the component of a child [Layout](/docs/app/api-reference/file-conventions/page) (if it exists) or [Page](/docs/app/api-reference/file-conventions/page), but could also be other special files like [Loading](/docs/app/api-reference/file-conventions/loading) or [Error](/docs/app/getting-started/error-handling) when applicable.
#### `params` (optional)
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) object from the root segment down to that layout.
```tsx filename="app/dashboard/[team]/layout.tsx" switcher
export default async function Layout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
}
```
```jsx filename="app/dashboard/[team]/layout.js" switcher
export default async function Layout({ children, params }) {
const { team } = await params
}
```
| Example Route | URL | `params` |
| --------------------------------- | -------------- | ---------------------------------- |
| `app/dashboard/[team]/layout.js` | `/dashboard/1` | `Promise<{ team: '1' }>` |
| `app/shop/[tag]/[item]/layout.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` |
| `app/blog/[...slug]/layout.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` |
- Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
### Layout Props Helper
You can type layouts with `LayoutProps` to get a strongly typed `params` and named slots inferred from your directory structure. `LayoutProps` is a globally available helper.
```tsx filename="app/dashboard/layout.tsx"
export default function Layout(props: LayoutProps<'/dashboard'>) {
return (
<section>
{props.children}
{/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
{/* {props.analytics} */}
</section>
)
}
```
> **Good to know**:
>
> - Types are generated during `next dev`, `next build` or `next typegen`.
> - After type generation, the `LayoutProps` helper is globally available. It doesn't need to be imported.
### Root Layout
The `app` directory **must** include a **root layout**, which is the top-most layout in the root `app` directory. Typically, the root layout is `app/layout.js`.
```tsx filename="app/layout.tsx" switcher
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>{children}</body>
</html>
)
}
```
```jsx filename="app/layout.js" switcher
export default function RootLayout({ children }) {
return (
<html>
<body>{children}</body>
</html>
)
}
```
- The root layout **must** define `<html>` and `<body>` tags.
- You should **not** manually add `<head>` tags such as `<title>` and `<meta>` to root layouts. Instead, you should use the [Metadata API](/docs/app/getting-started/metadata-and-og-images) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements.
- You can create **multiple root layouts**. Any layout without a `layout.js` above it is a root layout. Two common approaches:
- Using [route groups](/docs/app/api-reference/file-conventions/route-groups) like `app/(shop)/layout.js` and `app/(marketing)/layout.js`
- Omitting `app/layout.js` so layouts in subdirectories like `app/dashboard/layout.js` and `app/blog/layout.js` each become root layouts for their respective directories.
- Navigating **across multiple root layouts** will cause a **full page load** (as opposed to a client-side navigation).
- The root layout can be under a **dynamic segment**, for example when implementing [internationalization](/docs/app/guides/internationalization) with `app/[lang]/layout.js`.
## Caveats
### Request Object
Layouts are cached in the client during navigation to avoid unnecessary server requests.
[Layouts](/docs/app/api-reference/file-conventions/layout) do not rerender. They can be cached and reused to avoid unnecessary computation when navigating between pages. By restricting layouts from accessing the raw request, Next.js can prevent the execution of potentially slow or expensive user code within the layout, which could negatively impact performance.
To access the request object, you can use [`headers`](/docs/app/api-reference/functions/headers) and [`cookies`](/docs/app/api-reference/functions/cookies) APIs in [Server Components](/docs/app/getting-started/server-and-client-components) and Functions.
```tsx filename="app/shop/layout.tsx" switcher
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
```jsx filename="app/shop/layout.js" switcher
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
### Query params
Layouts do not rerender on navigation, so they cannot access search params which would otherwise become stale.
To access updated query parameters, you can use the Page [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop, or read them inside a Client Component using the [`useSearchParams`](/docs/app/api-reference/functions/use-search-params) hook. Since Client Components re-render on navigation, they have access to the latest query parameters.
```tsx filename="app/ui/search.tsx" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}
```
```jsx filename="app/ui/search.js" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}
```
```tsx filename="app/shop/layout.tsx" switcher
import Search from '@/app/ui/search'
export default function Layout({ children }) {
return (
<>
<Search />
{children}
</>
)
}
```
```jsx filename="app/shop/layout.js" switcher
import Search from '@/app/ui/search'
export default function Layout({ children }) {
return (
<>
<Search />
{children}
</>
)
}
```
### Pathname
Layouts do not re-render on navigation, so they do not access pathname which would otherwise become stale.
To access the current pathname, you can read it inside a Client Component using the [`usePathname`](/docs/app/api-reference/functions/use-pathname) hook. Since Client Components re-render during navigation, they have access to the latest pathname.
```tsx filename="app/ui/breadcrumbs.tsx" switcher
'use client'
import { usePathname } from 'next/navigation'
// Simplified breadcrumbs logic
export default function Breadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/')
return (
<nav>
{segments.map((segment, index) => (
<span key={index}>
{' > '}
{segment}
</span>
))}
</nav>
)
}
```
```jsx filename="app/ui/breadcrumbs.js" switcher
'use client'
import { usePathname } from 'next/navigation'
// Simplified breadcrumbs logic
export default function Breadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/')
return (
<nav>
{segments.map((segment, index) => (
<span key={index}>
{' > '}
{segment}
</span>
))}
</nav>
)
}
```
```tsx filename="app/docs/layout.tsx" switcher
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}
```
```jsx filename="app/docs/layout.js" switcher
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}
```
### Interaction with `loading.js`
Because `loading.js` sits below `layout.js` in the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), it cannot show a fallback for uncached or runtime data access in the layout itself, such as calling [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), or making uncached fetches.
The behavior depends on whether [Cache Components](/docs/app/getting-started/caching) is enabled:
- **Without Cache Components:** The navigation will block until the layout finishes rendering, and the `loading.js` fallback will not be shown.
- **With Cache Components:** `loading.js` is treated as a regular `<Suspense>` boundary rather than a special prefetch marker. Uncached or runtime data access in the layout must be explicitly wrapped in its own `<Suspense>` boundary, otherwise Next.js guides you with a build-time error. The static shell streams immediately, and the uncached content swaps in as it resolves.
In both cases, to ensure instant navigation, either:
- Wrap runtime data access in your layout in its own `<Suspense>` boundary with a fallback.
- Move uncached data fetching from `layout.js` into `page.js` where `loading.js` can show a fallback.
```tsx filename="app/dashboard/layout.tsx" switcher highlight={8-10}
import { Suspense } from 'react'
import { NavSkeleton } from './nav-skeleton'
import { DashboardNav } from './dashboard-nav'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<Suspense fallback={<NavSkeleton />}>
<DashboardNav />
</Suspense>
<main>{children}</main>
</>
)
}
```
```jsx filename="app/dashboard/layout.js" switcher highlight={8-10}
import { Suspense } from 'react'
import { NavSkeleton } from './nav-skeleton'
import { DashboardNav } from './dashboard-nav'
export default function Layout({ children }) {
return (
<>
<Suspense fallback={<NavSkeleton />}>
<DashboardNav />
</Suspense>
<main>{children}</main>
</>
)
}
```
### Fetching Data
Layouts cannot pass data to their `children`. However, you can fetch the same data in a route more than once, and use React [`cache`](https://react.dev/reference/react/cache) to dedupe the requests without affecting performance.
Alternatively, when using [`fetch`](/docs/app/api-reference/functions/fetch)in Next.js, requests are automatically deduped.
```tsx filename="app/lib/data.ts" switcher
export async function getUser(id: string) {
const res = await fetch(`https://.../users/${id}`)
return res.json()
}
```
```tsx filename="app/dashboard/layout.tsx" switcher
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Layout({ children }) {
const user = await getUser('1')
return (
<>
<nav>
{/* ... */}
<UserName user={user.name} />
</nav>
{children}
</>
)
}
```
```jsx filename="app/dashboard/layout.js" switcher
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Layout({ children }) {
const user = await getUser('1')
return (
<>
<nav>
{/* ... */}
<UserName user={user.name} />
</nav>
{children}
</>
)
}
```
```tsx filename="app/dashboard/page.tsx" switcher
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Page() {
const user = await getUser('1')
return (
<div>
<h1>Welcome {user.name}</h1>
</div>
)
}
```
```jsx filename="app/dashboard/page.js" switcher
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Page() {
const user = await getUser('1')
return (
<div>
<h1>Welcome {user.name}</h1>
</div>
)
}
```
### Accessing child segments
Layouts do not have access to the route segments below itself. To access all route segments, you can use [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) or [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) in a Client Component.
```tsx filename="app/ui/nav-link.tsx" switcher
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function NavLink({
slug,
children,
}: {
slug: string
children: React.ReactNode
}) {
const segment = useSelectedLayoutSegment()
const isActive = slug === segment
return (
<Link
href={`/blog/${slug}`}
// Change style depending on whether the link is active
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</Link>
)
}
```
```jsx filename="app/ui/nav-link.js" switcher
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function NavLinks({ slug, children }) {
const segment = useSelectedLayoutSegment()
const isActive = slug === segment
return (
<Link
href={`/blog/${slug}`}
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</Link>
)
}
```
```tsx filename="app/blog/layout.tsx" switcher
import { NavLink } from './nav-link'
import getPosts from './get-posts'
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const featuredPosts = await getPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<NavLink slug={post.slug}>{post.title}</NavLink>
</div>
))}
<div>{children}</div>
</div>
)
}
```
```jsx filename="app/blog/layout.js" switcher
import { NavLink } from './nav-link'
import getPosts from './get-posts'
export default async function Layout({ children }) {
const featuredPosts = await getPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<NavLink slug={post.slug}>{post.title}</NavLink>
</div>
))}
<div>{children}</div>
</div>
)
}
```
## Examples
### Metadata
You can modify the `<head>` HTML elements such as `title` and `meta` using the [`metadata` object](/docs/app/api-reference/functions/generate-metadata#the-metadata-object) or [`generateMetadata` function](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function).
```tsx filename="app/layout.tsx" switcher
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Layout({ children }: { children: React.ReactNode }) {
return '...'
}
```
```jsx filename="app/layout.js" switcher
export const metadata = {
title: 'Next.js',
}
export default function Layout({ children }) {
return '...'
}
```
> **Good to know**: You should **not** manually add `<head>` tags such as `<title>` and `<meta>` to root layouts. Instead, use the [Metadata APIs](/docs/app/api-reference/functions/generate-metadata) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements.
### Active Nav Links
You can use the [`usePathname`](/docs/app/api-reference/functions/use-pathname) hook to determine if a nav link is active.
Since `usePathname` is a client hook, you need to extract the nav links into a Client Component, which can be imported into your layout:
```tsx filename="app/ui/nav-links.tsx" switcher
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export function NavLinks() {
const pathname = usePathname()
return (
<nav>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</nav>
)
}
```
```jsx filename="app/ui/nav-links.js" switcher
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export function Links() {
const pathname = usePathname()
return (
<nav>
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === '/about' ? 'active' : ''}`}
href="/about"
>
About
</Link>
</nav>
)
}
```
```tsx filename="app/layout.tsx" switcher
import { NavLinks } from '@/app/ui/nav-links'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
)
}
```
```jsx filename="app/layout.js" switcher
import { NavLinks } from '@/app/ui/nav-links'
export default function Layout({ children }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
)
}
```
### Displaying content based on `params`
Using [dynamic route segments](/docs/app/api-reference/file-conventions/dynamic-routes), you can display or fetch specific content based on the `params` prop.
```tsx filename="app/dashboard/layout.tsx" switcher
export default async function DashboardLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
return (
<section>
<header>
<h1>Welcome to {team}'s Dashboard</h1>
</header>
<main>{children}</main>
</section>
)
}
```
```jsx filename="app/dashboard/layout.js" switcher
export default async function DashboardLayout({ children, params }) {
const { team } = await params
return (
<section>
<header>
<h1>Welcome to {team}'s Dashboard</h1>
</header>
<main>{children}</main>
</section>
)
}
```
### Reading `params` in Client Components
To use `params` in a Client Component (which cannot be `async`), you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise:
```tsx filename="app/page.tsx" switcher
'use client'
import { use } from 'react'
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
}
```
```js filename="app/page.js" switcher
'use client'
import { use } from 'react'
export default function Page({ params }) {
const { slug } = use(params)
}
```
## Version History
| Version | Changes |
| ------------ | --------------------------------------------------------------------------------------------- |
| `v15.0.0-RC` | `params` is now a promise. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
| `v13.0.0` | `layout` introduced. |
@@ -0,0 +1,196 @@
---
title: loading.js
description: API reference for the loading.js file.
---
{/* AI agent hint: loading.js provides fallback UI but does not guarantee instant client-side navigations. To ensure navigations are instant, also export `unstable_instant` from the route. See docs/01-app/02-guides/instant-navigation.mdx */}
The special file `loading.js` helps you create meaningful Loading UI with [React Suspense](https://react.dev/reference/react/Suspense). With this convention, you can show an [instant loading state](#instant-loading-states) from the server while the content of a route segment streams in. The new content is automatically swapped in once complete.
<Image
alt="Loading UI"
srcLight="/docs/light/loading-ui.png"
srcDark="/docs/dark/loading-ui.png"
width="1600"
height="691"
/>
```tsx filename="app/feed/loading.tsx" switcher
export default function Loading() {
// Or a custom loading skeleton component
return <p>Loading...</p>
}
```
```jsx filename="app/feed/loading.js" switcher
export default function Loading() {
// Or a custom loading skeleton component
return <p>Loading...</p>
}
```
Inside the `loading.js` file, you can add any light-weight loading UI. You may find it helpful to use the [React Developer Tools](https://react.dev/learn/react-developer-tools) to manually toggle Suspense boundaries.
By default, this file is a [Server Component](/docs/app/getting-started/server-and-client-components) - but can also be used as a Client Component through the `"use client"` directive.
## Reference
### Parameters
Loading UI components do not accept any parameters.
## Behavior
### Navigation
- The Fallback UI is [prefetched](/docs/app/getting-started/linking-and-navigating#prefetching), making navigation immediate unless prefetching hasn't completed.
- Navigation is interruptible, meaning changing routes does not need to wait for the content of the route to fully load before navigating to another route.
- Shared layouts remain interactive while new route segments load.
### Instant Loading States
An instant loading state is fallback UI that is shown immediately upon navigation. You can prerender loading indicators such as skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc. This helps users understand the app is responding and provides a better user experience.
Create a loading state by adding a `loading.js` file inside a folder.
<Image
alt="loading.js special file"
srcLight="/docs/light/loading-special-file.png"
srcDark="/docs/dark/loading-special-file.png"
width="1600"
height="606"
/>
```tsx filename="app/dashboard/loading.tsx" switcher
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
```
```jsx filename="app/dashboard/loading.js" switcher
export default function Loading() {
// You can add any UI inside Loading, including a Skeleton.
return <LoadingSkeleton />
}
```
In the same folder, `loading.js` will be nested inside `layout.js`. It will automatically wrap the `page.js` file and any children below in a `<Suspense>` boundary.
<Image
alt="loading.js overview"
srcLight="/docs/light/loading-overview.png"
srcDark="/docs/dark/loading-overview.png"
width="1600"
height="768"
/>
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `loading.js` wraps `not-found.js`, `page.js`, and nested `layout.js` files in a `<Suspense>` boundary. It does **not** wrap the `layout.js`, `template.js`, or `error.js` in the same segment.
> **Good to know**: If the layout accesses uncached or runtime data (e.g. `cookies()`, `headers()`, or uncached fetches), `loading.js` will not show a fallback for it.
>
> - **Without [Cache Components](/docs/app/getting-started/caching):** Navigation blocks until the layout finishes rendering.
> - **With [Cache Components](/docs/app/getting-started/caching):** Uncached or runtime data access in the layout must be explicitly wrapped in `<Suspense>`, otherwise Next.js guides you with a build-time error. The static shell streams first, and the uncached content fills in.
>
> To ensure instant navigation, move uncached data fetching from `layout.js` into `page.js`, or wrap the runtime data access in your layout in its own `<Suspense>` boundary. See [layout.js Caveats](/docs/app/api-reference/file-conventions/layout#interaction-with-loadingjs) for details and examples.
### SEO
- For bots that only scrape static HTML, and cannot execute JavaScript like a full browser, such as Twitterbot, Next.js resolves [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) before streaming UI, and metadata is placed in the `<head>` of the initial HTML.
- Otherwise, [streaming metadata](/docs/app/api-reference/functions/generate-metadata#streaming-metadata) may be used. Next.js automatically detects user agents to choose between blocking and streaming behavior.
- Since streaming is server-rendered, it does not impact SEO. You can use the [Rich Results Test](https://search.google.com/test/rich-results) tool from Google to see how your page appears to Google's web crawlers and view the serialized HTML ([source](https://web.dev/rendering-on-the-web/#seo-considerations)).
### Status Codes
When streaming, a `200` status code will be returned to signal that the request was successful.
The server can still communicate errors or issues to the client within the streamed content itself, for example, when using [`redirect`](/docs/app/api-reference/functions/redirect) or [`notFound`](/docs/app/api-reference/functions/not-found). Because the response headers have already been sent to the client, the status code of the response cannot be updated.
For example, when a 404 page is streamed to the client, Next.js includes a `<meta name="robots" content="noindex">` tag in the streamed HTML. This prevents search engines from indexing that URL even if the HTTP status is 200. See Googles guidance on the [`robots` meta tag](https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag).
Some crawlers may label these responses as “soft 404s”. In the streaming case, this does not lead to indexation because the page is explicitly marked `noindex` in the HTML.
If you need a 404 status, for compliance or analytics, ensure the resource exists before the response body is streamed, so that the server can set the HTTP status code.
You can run this check in [`proxy`](/docs/app/api-reference/file-conventions/proxy) to rewrite missing slugs to a not-found route, or [produce a 404 response](/docs/app/api-reference/file-conventions/proxy#producing-a-response). Keep proxy checks fast, and avoid fetching full content there.
<details>
<summary>When is the response body streamed?</summary>
The response body starts streaming when a Suspense fallback renders (for example, a `loading.tsx`) or when a Server Component suspends under a `Suspense` boundary. Place `notFound()` before those boundaries and before any `await` that may suspend.
To start streaming, the response headers must be set. This is why it is not possible to change the status code after streaming started.
</details>
### Browser limits
[Some browsers](https://bugs.webkit.org/show_bug.cgi?id=252413) buffer a streaming response. You may not see the streamed response until the response exceeds 1024 bytes. This typically only affects “hello world” applications, but not real applications.
## Platform Support
| Deployment Option | Supported |
| ------------------------------------------------------------------- | ----------------- |
| [Node.js server](/docs/app/getting-started/deploying#nodejs-server) | Yes |
| [Docker container](/docs/app/getting-started/deploying#docker) | Yes |
| [Static export](/docs/app/getting-started/deploying#static-export) | No |
| [Adapters](/docs/app/getting-started/deploying#adapters) | Platform-specific |
Learn how to [configure streaming](/docs/app/guides/self-hosting#streaming-and-suspense) when self-hosting Next.js.
## Examples
### Streaming with Suspense
In addition to `loading.js`, you can also manually create Suspense Boundaries for your own UI components. The App Router supports streaming with [Suspense](https://react.dev/reference/react/Suspense). See the [Streaming guide](/docs/app/guides/streaming) for more on how streaming works, including granular Suspense patterns, Route Handler streaming, and infrastructure considerations.
`<Suspense>` works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes.
```tsx filename="app/dashboard/page.tsx" switcher
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
```
```jsx filename="app/dashboard/page.js" switcher
import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'
export default function Posts() {
return (
<section>
<Suspense fallback={<p>Loading feed...</p>}>
<PostFeed />
</Suspense>
<Suspense fallback={<p>Loading weather...</p>}>
<Weather />
</Suspense>
</section>
)
}
```
By using Suspense, you get the benefits of:
1. **Streaming Server Rendering** - Progressively rendering HTML from the server to the client.
2. **Selective Hydration** - React prioritizes what components to make interactive first based on user interaction.
For more Suspense examples and use cases, please see the [React Documentation](https://react.dev/reference/react/Suspense).
## Version History
| Version | Changes |
| --------- | --------------------- |
| `v13.0.0` | `loading` introduced. |
@@ -0,0 +1,60 @@
---
title: mdx-components.js
description: API reference for the mdx-components.js file.
related:
title: Learn more about MDX Components
links:
- app/guides/mdx
---
The `mdx-components.js|tsx` file is **required** to use [`@next/mdx` with App Router](/docs/app/guides/mdx) and will not work without it. Additionally, you can use it to [customize styles](/docs/app/guides/mdx#using-custom-styles-and-components).
Use the file `mdx-components.tsx` (or `.js`) in the root of your project to define MDX Components. For example, at the same level as `pages` or `app`, or inside `src` if applicable.
```tsx filename="mdx-components.tsx" switcher
import type { MDXComponents } from 'mdx/types'
const components: MDXComponents = {}
export function useMDXComponents(): MDXComponents {
return components
}
```
```js filename="mdx-components.js" switcher
const components = {}
export function useMDXComponents() {
return components
}
```
## Exports
### `useMDXComponents` function
The file must export a single function named `useMDXComponents`. This function does not accept any arguments.
```tsx filename="mdx-components.tsx" switcher
import type { MDXComponents } from 'mdx/types'
const components: MDXComponents = {}
export function useMDXComponents(): MDXComponents {
return components
}
```
```js filename="mdx-components.js" switcher
const components = {}
export function useMDXComponents() {
return components
}
```
## Version History
| Version | Changes |
| --------- | -------------------- |
| `v13.1.2` | MDX Components added |
@@ -0,0 +1,235 @@
---
title: not-found.js
description: API reference for the not-found.js file.
---
Next.js provides two conventions to handle not found cases:
- **`not-found.js`**: Used when you call the [`notFound`](/docs/app/api-reference/functions/not-found) function in a route segment.
- **`global-not-found.js`**: Used to define a global 404 page for unmatched routes across your entire app. This is handled at the routing level and doesn't depend on rendering a layout or page.
## `not-found.js`
The **not-found** file is used to render UI when the [`notFound`](/docs/app/api-reference/functions/not-found) function is thrown within a route segment. Along with serving a custom UI, Next.js will return a `200` HTTP status code for streamed responses, and `404` for non-streamed responses (see [Status Codes](/docs/app/api-reference/file-conventions/loading#status-codes) for details about SEO).
```tsx filename="app/not-found.tsx" switcher
import Link from 'next/link'
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
</div>
)
}
```
```jsx filename="app/blog/not-found.js" switcher
import Link from 'next/link'
export default function NotFound() {
return (
<div>
<h2>Not Found</h2>
<p>Could not find requested resource</p>
<Link href="/">Return Home</Link>
</div>
)
}
```
In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `not-found.js` renders between `loading.js` and `page.js`. It is wrapped by the `<Suspense>` boundary from `loading.js` and the error boundary from `error.js` in the same segment.
## `global-not-found.js` (experimental)
The `global-not-found.js` file lets you define a 404 page for your entire application. Unlike `not-found.js`, which works at the route level, this is used when a requested URL doesn't match any route at all. Next.js **skips rendering** and directly returns this global page.
The `global-not-found.js` file bypasses your app's normal rendering, which means you'll need to import any global styles, fonts, or other dependencies that your 404 page requires.
> **Good to know**: A smaller version of your global styles, and a simpler font family could improve performance of this page.
`global-not-found.js` is useful when you can't build a 404 page using a combination of `layout.js` and `not-found.js`. This can happen in two cases:
- Your app has multiple root layouts (e.g. `app/(admin)/layout.tsx` and `app/(shop)/layout.tsx`), so there's no single layout to compose a global 404 from.
- Your root layout is defined using top-level dynamic segments (e.g. `app/[country]/layout.tsx`), which makes composing a consistent 404 page harder.
To enable it, add the `globalNotFound` flag in `next.config.ts`:
```tsx filename="next.config.ts"
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
globalNotFound: true,
},
}
export default nextConfig
```
Then, create a file in the root of the `app` directory: `app/global-not-found.js`:
```tsx filename="app/global-not-found.tsx" switcher
// Import global styles and fonts
import './globals.css'
import { Inter } from 'next/font/google'
import type { Metadata } from 'next'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: '404 - Page Not Found',
description: 'The page you are looking for does not exist.',
}
export default function GlobalNotFound() {
return (
<html lang="en" className={inter.className}>
<body>
<h1>404 - Page Not Found</h1>
<p>This page does not exist.</p>
</body>
</html>
)
}
```
```jsx filename="app/global-not-found.js" switcher
// Import global styles and fonts
import './globals.css'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: '404 - Page Not Found',
description: 'The page you are looking for does not exist.',
}
export default function GlobalNotFound() {
return (
<html lang="en" className={inter.className}>
<body>
<h1>404 - Page Not Found</h1>
<p>This page does not exist.</p>
</body>
</html>
)
}
```
Unlike `not-found.js`, this file must return a full HTML document, including `<html>` and `<body>` tags.
## Reference
### Props
`not-found.js` or `global-not-found.js` components do not accept any props.
> **Good to know**: In addition to catching expected `notFound()` errors, the root `app/not-found.js` and `app/global-not-found.js` files handle any unmatched URLs for your whole application. This means users that visit a URL that is not handled by your app will be shown the exported UI.
## Examples
### Data Fetching
By default, `not-found` is a Server Component. You can mark it as `async` to fetch and display data:
```tsx filename="app/not-found.tsx" switcher
import Link from 'next/link'
import { headers } from 'next/headers'
export default async function NotFound() {
const headersList = await headers()
const domain = headersList.get('host')
const data = await getSiteData(domain)
return (
<div>
<h2>Not Found: {data.name}</h2>
<p>Could not find requested resource</p>
<p>
View <Link href="/blog">all posts</Link>
</p>
</div>
)
}
```
```jsx filename="app/not-found.jsx" switcher
import Link from 'next/link'
import { headers } from 'next/headers'
export default async function NotFound() {
const headersList = await headers()
const domain = headersList.get('host')
const data = await getSiteData(domain)
return (
<div>
<h2>Not Found: {data.name}</h2>
<p>Could not find requested resource</p>
<p>
View <Link href="/blog">all posts</Link>
</p>
</div>
)
}
```
If you need to use Client Component hooks like `usePathname` to display content based on the path, you must fetch data on the client-side instead.
### Metadata
For `global-not-found.js`, you can export a `metadata` object or a [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) function to customize the `<title>`, `<meta>`, and other head tags for your 404 page:
> **Good to know**: Next.js automatically injects `<meta name="robots" content="noindex" />` for pages that return a 404 status code, including `global-not-found.js` pages.
```tsx filename="app/global-not-found.tsx" switcher
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Not Found',
description: 'The page you are looking for does not exist.',
}
export default function GlobalNotFound() {
return (
<html lang="en">
<body>
<div>
<h1>Not Found</h1>
<p>The page you are looking for does not exist.</p>
</div>
</body>
</html>
)
}
```
```jsx filename="app/global-not-found.js" switcher
export const metadata = {
title: 'Not Found',
description: 'The page you are looking for does not exist.',
}
export default function GlobalNotFound() {
return (
<html lang="en">
<body>
<div>
<h1>Not Found</h1>
<p>The page you are looking for does not exist.</p>
</div>
</body>
</html>
)
}
```
## Version History
| Version | Changes |
| --------- | --------------------------------------------------- |
| `v15.4.0` | `global-not-found.js` introduced (experimental). |
| `v13.3.0` | Root `app/not-found` handles global unmatched URLs. |
| `v13.0.0` | `not-found` introduced. |
@@ -0,0 +1,240 @@
---
title: page.js
description: API reference for the page.js file.
---
The `page` file allows you to define UI that is **unique** to a route. You can create a page by default exporting a component from the file:
```tsx filename="app/blog/[slug]/page.tsx" switcher
export default function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
return <h1>My Page</h1>
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
export default function Page({ params, searchParams }) {
return <h1>My Page</h1>
}
```
## Good to know
- The `.js`, `.jsx`, or `.tsx` file extensions can be used for `page`.
- A `page` is always the **leaf** of the route subtree.
- A `page` file is required to make a route segment **publicly accessible**.
- Pages are [Server Components](https://react.dev/reference/rsc/server-components) by default, but can be set to a [Client Component](https://react.dev/reference/rsc/use-client).
- In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `page.js` is the innermost file convention. It is wrapped by `loading.js` (Suspense boundary), `error.js` (error boundary), `template.js`, and `layout.js` in the same segment.
## Reference
### Props
#### `params` (optional)
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) from the root segment down to that page.
```tsx filename="app/shop/[slug]/page.tsx" switcher
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
}
```
```jsx filename="app/shop/[slug]/page.js" switcher
export default async function Page({ params }) {
const { slug } = await params
}
```
| Example Route | URL | `params` |
| ------------------------------------ | ----------- | --------------------------------------- |
| `app/shop/[slug]/page.js` | `/shop/1` | `Promise<{ slug: '1' }>` |
| `app/shop/[category]/[item]/page.js` | `/shop/1/2` | `Promise<{ category: '1', item: '2' }>` |
| `app/shop/[...slug]/page.js` | `/shop/1/2` | `Promise<{ slug: ['1', '2'] }>` |
- Since the `params` prop is a promise, you must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
#### `searchParams` (optional)
A promise that resolves to an object containing the [search parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters) of the current URL. For example:
```tsx filename="app/shop/page.tsx" switcher
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const filters = (await searchParams).filters
}
```
```jsx filename="app/shop/page.js" switcher
export default async function Page({ searchParams }) {
const filters = (await searchParams).filters
}
```
Client Component **pages** can also access `searchParams` using Reacts [`use`](https://react.dev/reference/react/use) hook:
```tsx filename="app/shop/page.tsx" switcher
'use client'
import { use } from 'react'
export default function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const filters = use(searchParams).filters
}
```
```jsx filename="app/page.jsx" switcher
'use client'
import { use } from 'react'
export default function Page({ searchParams }) {
const filters = use(searchParams).filters
}
```
| Example URL | `searchParams` |
| --------------- | ----------------------------- |
| `/shop?a=1` | `Promise<{ a: '1' }>` |
| `/shop?a=1&b=2` | `Promise<{ a: '1', b: '2' }>` |
| `/shop?a=1&a=2` | `Promise<{ a: ['1', '2'] }>` |
- Since the `searchParams` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
- In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
- `searchParams` is a **[Request-time API](/docs/app/glossary#request-time-apis)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/glossary#dynamic-rendering)** at request time.
- `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance.
### Page Props Helper
You can type pages with `PageProps` to get strongly typed `params` and `searchParams` from the route literal. `PageProps` is a globally available helper.
```tsx filename="app/blog/[slug]/page.tsx"
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params
const query = await props.searchParams
return <h1>Blog Post: {slug}</h1>
}
```
> **Good to know**
>
> - Using a literal route (e.g. `'/blog/[slug]'`) enables autocomplete and strict keys for `params`.
> - Static routes resolve `params` to `{}`.
> - Types are generated during `next dev`, `next build`, or with `next typegen`.
> - After type generation, the `PageProps` helper is globally available. It doesn't need to be imported.
## Examples
### Displaying content based on `params`
Using [dynamic route segments](/docs/app/api-reference/file-conventions/dynamic-routes), you can display or fetch specific content for the page based on the `params` prop.
```tsx filename="app/blog/[slug]/page.tsx" switcher
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
return <h1>Blog Post: {slug}</h1>
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
export default async function Page({ params }) {
const { slug } = await params
return <h1>Blog Post: {slug}</h1>
}
```
### Handling filtering with `searchParams`
You can use the `searchParams` prop to handle filtering, pagination, or sorting based on the query string of the URL.
```tsx filename="app/shop/page.tsx" switcher
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const { page = '1', sort = 'asc', query = '' } = await searchParams
return (
<div>
<h1>Product Listing</h1>
<p>Search query: {query}</p>
<p>Current page: {page}</p>
<p>Sort order: {sort}</p>
</div>
)
}
```
```jsx filename="app/shop/page.js" switcher
export default async function Page({ searchParams }) {
const { page = '1', sort = 'asc', query = '' } = await searchParams
return (
<div>
<h1>Product Listing</h1>
<p>Search query: {query}</p>
<p>Current page: {page}</p>
<p>Sort order: {sort}</p>
</div>
)
}
```
### Reading `searchParams` and `params` in Client Components
To use `searchParams` and `params` in a Client Component (which cannot be `async`), you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise:
```tsx filename="app/page.tsx" switcher
'use client'
import { use } from 'react'
export default function Page({
params,
searchParams,
}: {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const { slug } = use(params)
const { query } = use(searchParams)
}
```
```js filename="app/page.js" switcher
'use client'
import { use } from 'react'
export default function Page({ params, searchParams }) {
const { slug } = use(params)
const { query } = use(searchParams)
}
```
## Version History
| Version | Changes |
| ------------ | ---------------------------------------------------------------------------------------------------------------- |
| `v15.0.0-RC` | `params` and `searchParams` are now promises. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
| `v13.0.0` | `page` introduced. |
@@ -0,0 +1,480 @@
---
title: Parallel Routes
description: Simultaneously render one or more pages in the same view that can be navigated independently. A pattern for highly dynamic applications.
related:
links:
- app/api-reference/file-conventions/default
---
Parallel Routes allows you to simultaneously or conditionally render one or more pages within the same layout. They are useful for highly dynamic sections of an app, such as dashboards and feeds on social sites.
For example, considering a dashboard, you can use parallel routes to simultaneously render the `team` and `analytics` pages:
<Image
alt="Parallel Routes Diagram"
srcLight="/docs/light/parallel-routes.png"
srcDark="/docs/dark/parallel-routes.png"
width="1600"
height="942"
/>
## Convention
### Slots
Parallel routes are created using named **slots**. Slots are defined with the `@folder` convention. For example, the following file structure defines two slots: `@analytics` and `@team`:
<Image
alt="Parallel Routes File-system Structure"
srcLight="/docs/light/parallel-routes-file-system.png"
srcDark="/docs/dark/parallel-routes-file-system.png"
width="1600"
height="687"
/>
Slots are passed as props to the shared parent layout. For the example above, the component in `app/layout.js` now accepts the `@analytics` and `@team` slots props, and can render them in parallel alongside the `children` prop:
```tsx filename="app/layout.tsx" switcher
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
```
```jsx filename="app/layout.js" switcher
export default function Layout({ children, team, analytics }) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
```
However, slots are **not** route segments and do not affect the URL structure. For example, for `/@analytics/views`, the URL will be `/views` since `@analytics` is a slot. Slots are combined with the regular [Page](/docs/app/api-reference/file-conventions/page) component to form the final page associated with the route segment. Because of this, you cannot have separate [prerendered](/docs/app/glossary#prerendering) and [dynamically rendered](/docs/app/glossary#dynamic-rendering) slots at the same route segment level. If one slot is dynamic, all slots at that level must be dynamic.
> **Good to know**:
>
> - The `children` prop is an implicit slot that does not need to be mapped to a folder. This means `app/page.js` is equivalent to `app/@children/page.js`.
### `default.js`
You can define a `default.js` file to render as a fallback for unmatched slots during the initial load or full-page reload.
Consider the following folder structure. The `@team` slot has a `/settings` page, but `@analytics` does not.
<Image
alt="Parallel Routes unmatched routes"
srcLight="/docs/light/parallel-routes-unmatched-routes.png"
srcDark="/docs/dark/parallel-routes-unmatched-routes.png"
width="1600"
height="930"
/>
When navigating to `/settings`, the `@team` slot will render the `/settings` page while maintaining the currently active page for the `@analytics` slot.
On refresh, Next.js will render a `default.js` for `@analytics`. If `default.js` doesn't exist, a `404` is rendered instead.
Additionally, since `children` is an implicit slot, you also need to create a `default.js` file to render a fallback for `children` when Next.js cannot recover the active state of the parent page.
## Behavior
By default, Next.js keeps track of the active _state_ (or subpage) for each slot. However, the content rendered within a slot will depend on the type of navigation:
- [**Soft Navigation**](/docs/app/getting-started/linking-and-navigating#client-side-transitions): During client-side navigation, Next.js will perform a [partial render](/docs/app/getting-started/linking-and-navigating#client-side-transitions), changing the subpage within the slot, while maintaining the other slot's active subpages, even if they don't match the current URL.
- **Hard Navigation**: After a full-page load (browser refresh), Next.js cannot determine the active state for the slots that don't match the current URL. Instead, it will render a [`default.js`](#defaultjs) file for the unmatched slots, or `404` if `default.js` doesn't exist.
> **Good to know**:
>
> - The `404` for unmatched routes helps ensure that you don't accidentally render a parallel route on a page that it was not intended for.
## Examples
### With `useSelectedLayoutSegment(s)`
Both [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) and [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) accept a `parallelRoutesKey` parameter, which allows you to read the active route segment within a slot.
```tsx filename="app/layout.tsx" switcher
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
```
```jsx filename="app/layout.js" switcher
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
```
When a user navigates to `app/@auth/login` (or `/login` in the URL bar), `loginSegment` will be equal to the string `"login"`.
### Conditional Routes
You can use Parallel Routes to conditionally render routes based on certain conditions, such as user role. For example, to render a different dashboard page for the `/admin` or `/user` roles:
<Image
alt="Conditional routes diagram"
srcLight="/docs/light/conditional-routes-ui.png"
srcDark="/docs/dark/conditional-routes-ui.png"
width="1600"
height="898"
/>
```tsx filename="app/dashboard/layout.tsx" switcher
import { checkUserRole } from '@/lib/auth'
export default function Layout({
user,
admin,
}: {
user: React.ReactNode
admin: React.ReactNode
}) {
const role = checkUserRole()
return role === 'admin' ? admin : user
}
```
```jsx filename="app/dashboard/layout.js" switcher
import { checkUserRole } from '@/lib/auth'
export default function Layout({ user, admin }) {
const role = checkUserRole()
return role === 'admin' ? admin : user
}
```
### Tab Groups
You can add a `layout` inside a slot to allow users to navigate the slot independently. This is useful for creating tabs.
For example, the `@analytics` slot has two subpages: `/page-views` and `/visitors`.
<Image
alt="Analytics slot with two subpages and a layout"
srcLight="/docs/light/parallel-routes-tab-groups.png"
srcDark="/docs/dark/parallel-routes-tab-groups.png"
width="1600"
height="768"
/>
Within `@analytics`, create a [`layout`](/docs/app/api-reference/file-conventions/layout) file to share the tabs between the two pages:
```tsx filename="app/@analytics/layout.tsx" switcher
import Link from 'next/link'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
```
```jsx filename="app/@analytics/layout.js" switcher
import Link from 'next/link'
export default function Layout({ children }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
```
### Modals
Parallel Routes can be used together with [Intercepting Routes](/docs/app/api-reference/file-conventions/intercepting-routes) to create modals that support deep linking. This allows you to solve common challenges when building modals, such as:
- Making the modal content **shareable through a URL**.
- **Preserving context** when the page is refreshed, instead of closing the modal.
- **Closing the modal on backwards navigation** rather than going to the previous route.
- **Reopening the modal on forwards navigation**.
Consider the following UI pattern, where a user can open a login modal from a layout using client-side navigation, or access a separate `/login` page:
<Image
alt="Parallel Routes Diagram"
srcLight="/docs/light/parallel-routes-auth-modal.png"
srcDark="/docs/dark/parallel-routes-auth-modal.png"
width="1600"
height="687"
/>
To implement this pattern, start by creating a `/login` route that renders your **main** login page.
<Image
alt="Parallel Routes Diagram"
srcLight="/docs/light/parallel-routes-modal-login-page.png"
srcDark="/docs/dark/parallel-routes-modal-login-page.png"
width="1600"
height="768"
/>
```tsx filename="app/login/page.tsx" switcher
import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
```
```jsx filename="app/login/page.js" switcher
import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
```
Then, inside the `@auth` slot, add [`default.js`](/docs/app/api-reference/file-conventions/default) file that returns `null`. This ensures that the modal is not rendered when it's not active.
```tsx filename="app/@auth/default.tsx" switcher
export default function Default() {
return null
}
```
```jsx filename="app/@auth/default.js" switcher
export default function Default() {
return null
}
```
Inside your `@auth` slot, intercept the `/login` route by importing the `<Modal>` component and its children into the `@auth/(.)login/page.tsx` file, and updating the folder name to `/@auth/(.)login/page.tsx`.
```tsx filename="app/@auth/(.)login/page.tsx" switcher
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
```
```jsx filename="app/@auth/(.)login/page.js" switcher
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
```
> **Good to know:**
>
> - The convention `(.)` is used for intercepting routes. See [Intercepting Routes](/docs/app/api-reference/file-conventions/intercepting-routes#convention) docs for more information.
> - By separating the `<Modal>` functionality from the modal content (`<Login>`), you can ensure any content inside the modal, e.g. [forms](/docs/app/guides/forms), are Server Components. See [Interleaving Client and Server Components](/docs/app/getting-started/server-and-client-components#interleaving-server-and-client-components) for more information.
#### Opening the modal
Now, you can leverage the Next.js router to open and close the modal. This ensures the URL is correctly updated when the modal is open, and when navigating backwards and forwards.
To open the modal, pass the `@auth` slot as a prop to the parent layout and render it alongside the `children` prop.
```tsx filename="app/layout.tsx" switcher
import Link from 'next/link'
export default function Layout({
auth,
children,
}: {
auth: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<nav>
<Link href="/login">Open modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
```
```jsx filename="app/layout.js" switcher
import Link from 'next/link'
export default function Layout({ auth, children }) {
return (
<>
<nav>
<Link href="/login">Open modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
```
When the user clicks the `<Link>`, the modal will open instead of navigating to the `/login` page. However, on refresh or initial load, navigating to `/login` will take the user to the main login page.
#### Closing the modal
You can close the modal by calling `router.back()` or by using the `Link` component.
```tsx filename="app/ui/modal.tsx" switcher
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Close modal
</button>
<div>{children}</div>
</>
)
}
```
```jsx filename="app/ui/modal.js" switcher
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Close modal
</button>
<div>{children}</div>
</>
)
}
```
When using the `Link` component to navigate away from a page that shouldn't render the `@auth` slot anymore, we need to make sure the parallel route matches to a component that returns `null`. For example, when navigating back to the root page, we create a `@auth/page.tsx` component:
```tsx filename="app/ui/modal.tsx" switcher
import Link from 'next/link'
export function Modal({ children }: { children: React.ReactNode }) {
return (
<>
<Link href="/">Close modal</Link>
<div>{children}</div>
</>
)
}
```
```jsx filename="app/ui/modal.js" switcher
import Link from 'next/link'
export function Modal({ children }) {
return (
<>
<Link href="/">Close modal</Link>
<div>{children}</div>
</>
)
}
```
```tsx filename="app/@auth/page.tsx" switcher
export default function Page() {
return null
}
```
```jsx filename="app/@auth/page.js" switcher
export default function Page() {
return null
}
```
Or if navigating to any other page (such as `/foo`, `/foo/bar`, etc), you can use a catch-all slot:
```tsx filename="app/@auth/[...catchAll]/page.tsx" switcher
export default function CatchAll() {
return null
}
```
```jsx filename="app/@auth/[...catchAll]/page.js" switcher
export default function CatchAll() {
return null
}
```
> **Good to know:**
>
> - We use a catch-all route in our `@auth` slot to close the modal because of how parallel routes behave. Since client-side navigations to a route that no longer match the slot will remain visible, we need to match the slot to a route that returns `null` to close the modal.
> - Other examples could include opening a photo modal in a gallery while also having a dedicated `/photo/[id]` page, or opening a shopping cart in a side modal.
> - [View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes.
### Loading and Error UI
Parallel Routes can be streamed independently, allowing you to define independent error and loading states for each route:
<Image
alt="Parallel routes enable custom error and loading states"
srcLight="/docs/light/parallel-routes-cinematic-universe.png"
srcDark="/docs/dark/parallel-routes-cinematic-universe.png"
width="1600"
height="1218"
/>
See the [Loading UI](/docs/app/api-reference/file-conventions/loading) and [Error Handling](/docs/app/getting-started/error-handling) documentation for more information.
@@ -0,0 +1,777 @@
---
title: proxy.js
description: API reference for the proxy.js file.
related:
title: Learn more about Proxy
links:
- app/api-reference/functions/next-request
- app/api-reference/functions/next-response
---
> **Note**: The `middleware` file convention is deprecated and has been renamed to `proxy`. See [Migration to Proxy](#migration-to-proxy) for more details.
The `proxy.js|ts` file is used to write [Proxy](/docs/app/getting-started/proxy) and run code on the server before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.
Proxy executes before routes are rendered. It's particularly useful for implementing custom server-side logic like authentication, logging, or handling redirects.
> **Good to know**:
>
> Proxy is meant to be invoked separately of your render code and in optimized cases deployed to your CDN for fast redirect/rewrite handling, you should not attempt relying on shared modules or globals.
>
> To pass information from Proxy to your application, use [headers](#setting-headers), [cookies](#using-cookies), [rewrites](/docs/app/api-reference/functions/next-response#rewrite), [redirects](/docs/app/api-reference/functions/next-response#redirect), or the URL.
Create a `proxy.ts` (or `.js`) file in the project root, or inside `src` if applicable, so that it is located at the same level as `pages` or `app`.
If youve customized [`pageExtensions`](/docs/app/api-reference/config/next-config-js/pageExtensions), for example to `.page.ts` or `.page.js`, name your file `proxy.page.ts` or `proxy.page.js` accordingly.
```tsx filename="proxy.ts" switcher
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function proxy(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
```
```js filename="proxy.js" switcher
import { NextResponse } from 'next/server'
// This function can be marked `async` if using `await` inside
export function proxy(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
```
## Exports
### Proxy function
The file must export a single function, either as a default export or named `proxy`. Note that multiple proxy from the same file are not supported.
```js filename="proxy.js"
// Example of default export
export default function proxy(request) {
// Proxy logic
}
```
### Config object (optional)
Optionally, a config object can be exported alongside the Proxy function. This object includes the [matcher](#matcher) to specify paths where the Proxy applies.
### Matcher
The `matcher` option allows you to target specific paths for the Proxy to run on. You can specify these paths in several ways:
- For a single path: Directly use a string to define the path, like `'/about'`.
- For multiple paths: Use an array to list multiple paths, such as `matcher: ['/about', '/contact']`, which applies the Proxy to both `/about` and `/contact`.
```js filename="proxy.js"
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
```
Additionally, the `matcher` option supports complex path specifications using regular expressions. For example, you can exclude certain paths with a regular expression matcher:
```js filename="proxy.js"
export const config = {
matcher: [
// Exclude API routes, static files, image optimizations, and .png files
'/((?!api|_next/static|_next/image|.*\\.png$).*)',
],
}
```
This enables precise control over which paths to include or exclude.
The `matcher` option accepts an array of objects with the following keys:
- `source`: The path or pattern used to match the request paths. It can be a string for direct path matching or a pattern for more complex matching.
- `locale` (optional): A boolean that, when set to `false`, ignores locale-based routing in path matching.
- `has` (optional): Specifies conditions based on the presence of specific request elements such as headers, query parameters, or cookies.
- `missing` (optional): Focuses on conditions where certain request elements are absent, like missing headers or cookies.
```js filename="proxy.js"
export const config = {
matcher: [
{
source: '/api/:path*',
locale: false,
has: [
{ type: 'header', key: 'Authorization', value: 'Bearer Token' },
{ type: 'query', key: 'userId', value: '123' },
],
missing: [{ type: 'cookie', key: 'session', value: 'active' }],
},
],
}
```
The `source` path patterns:
1. MUST start with `/`
2. Can include named parameters: `/about/:path` matches `/about/a` and `/about/b` but not `/about/a/c`
3. Can have modifiers on named parameters (starting with `:`): `/about/:path*` matches `/about/a/b/c` because `*` is _zero or more_. `?` is _zero or one_ and `+` _one or more_
4. Can use regular expression enclosed in parenthesis: `/about/(.*)` is the same as `/about/:path*`
5. Are anchored to the start of the path: `/about` matches `/about` and `/about/team` but not `/blog/about`
Read more details on [path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp-1) documentation.
> **Good to know**:
>
> - The `matcher` values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored.
> - For backward compatibility, Next.js always considers `/public` as `/public/index`. Therefore, a matcher of `/public/:path` will match.
## Params
### `request`
When defining Proxy, the default export function accepts a single parameter, `request`. This parameter is an instance of `NextRequest`, which represents the incoming HTTP request.
```tsx filename="proxy.ts" switcher
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Proxy logic goes here
}
```
```js filename="proxy.js" switcher
export function proxy(request) {
// Proxy logic goes here
}
```
If you prefer a shorthand, you can use the `NextProxy` type. It infers the parameter types for both `request` (`NextRequest`) and `event` (`NextFetchEvent`) automatically:
```tsx filename="proxy.ts"
import type { NextProxy } from 'next/server'
export const proxy: NextProxy = (request, event) => {
event.waitUntil(Promise.resolve())
return Response.json({ pathname: request.nextUrl.pathname })
}
```
> **Good to know**:
>
> - `NextRequest` is a type that represents incoming HTTP requests in Next.js Proxy, whereas [`NextResponse`](#nextresponse) is a class used to manipulate and send back HTTP responses.
## NextResponse
The `NextResponse` API allows you to:
- `redirect` the incoming request to a different URL
- `rewrite` the response by displaying a given URL
- Set request headers for API Routes, `getServerSideProps`, and `rewrite` destinations
- Set response cookies
- Set response headers
<AppOnly>
To produce a response from Proxy, you can:
1. `rewrite` to a route ([Page](/docs/app/api-reference/file-conventions/page) or [Route Handler](/docs/app/api-reference/file-conventions/route)) that produces a response
2. return a `NextResponse` directly. See [Producing a Response](#producing-a-response)
> **Good to know**: For redirects, you can also use `Response.redirect` instead of `NextResponse.redirect`.
</AppOnly>
<PagesOnly>
To produce a response from Proxy, you can:
1. `rewrite` to a route ([Page](/docs/pages/building-your-application/routing/pages-and-layouts) or [Edge API Route](/docs/pages/building-your-application/routing/api-routes)) that produces a response
2. return a `NextResponse` directly. See [Producing a Response](#producing-a-response)
</PagesOnly>
## Execution order
Proxy will be invoked for **every route in your project**. Given this, it's crucial to use [matchers](#matcher) to precisely target or exclude specific routes. The following is the execution order:
1. `headers` from `next.config.js`
2. `redirects` from `next.config.js`
3. Proxy (`rewrites`, `redirects`, etc.)
4. `beforeFiles` (`rewrites`) from `next.config.js`
5. Filesystem routes (`public/`, `_next/static/`, `pages/`, `app/`, etc.)
6. `afterFiles` (`rewrites`) from `next.config.js`
7. Dynamic Routes (`/blog/[slug]`)
8. `fallback` (`rewrites`) from `next.config.js`
> **Good to know:** [Server Functions](/docs/app/api-reference/directives/use-server) are not separate routes in this chain. They are handled as POST requests to the route where they are used, so a Proxy matcher that excludes a path will also skip Server Function calls on that path.
>
> A matcher change or a refactor that moves a Server Function to a different route can silently remove Proxy coverage. Always verify authentication and authorization inside each Server Function rather than relying on Proxy alone. See the [Data Security guide](/docs/app/guides/data-security#authentication-and-authorization) for recommended patterns.
## Runtime
Proxy defaults to using the Node.js runtime. The [`runtime`](/docs/app/api-reference/file-conventions/route-segment-config/runtime) config option is not available in Proxy files. Setting the `runtime` config option in Proxy will throw an error.
## Advanced Proxy flags
In `v13.1` of Next.js two additional flags were introduced for proxy, `skipProxyUrlNormalize` (formerly `skipMiddlewareUrlNormalize`) and `skipTrailingSlashRedirect` to handle advanced use cases.
`skipTrailingSlashRedirect` disables Next.js redirects for adding or removing trailing slashes. This allows custom handling inside proxy to maintain the trailing slash for some paths but not others, which can make incremental migrations easier.
```js filename="next.config.js"
module.exports = {
skipTrailingSlashRedirect: true,
}
```
```js filename="proxy.js"
const legacyPrefixes = ['/docs', '/blog']
export default async function proxy(req) {
const { pathname } = req.nextUrl
if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}
// apply trailing slash handling
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
return NextResponse.redirect(
new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
)
}
}
```
`skipProxyUrlNormalize` allows for disabling the URL normalization in Next.js to make handling direct visits and client-transitions the same. In some advanced cases, this option provides full control by using the original URL.
```js filename="next.config.js"
module.exports = {
skipProxyUrlNormalize: true,
}
```
```js filename="proxy.js"
export default async function proxy(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// with the flag this now /_next/data/build-id/hello.json
// without the flag this would be normalized to /hello
}
```
## Examples
### Conditional Statements
```ts filename="proxy.ts" switcher
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
```
```js filename="proxy.js" switcher
import { NextResponse } from 'next/server'
export function proxy(request) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
```
### Using Cookies
Cookies are regular headers. On a `Request`, they are stored in the `Cookie` header. On a `Response` they are in the `Set-Cookie` header. Next.js provides a convenient way to access and manipulate these cookies through the `cookies` extension on `NextRequest` and `NextResponse`.
1. For incoming requests, `cookies` comes with the following methods: `get`, `getAll`, `set`, and `delete` cookies. You can check for the existence of a cookie with `has` or remove all cookies with `clear`.
2. For outgoing responses, `cookies` have the following methods `get`, `getAll`, `set`, and `delete`.
```ts filename="proxy.ts" switcher
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
return response
}
```
```js filename="proxy.js" switcher
import { NextResponse } from 'next/server'
export function proxy(request) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.
return response
}
```
### Setting Headers
You can set request and response headers using the `NextResponse` API (setting _request_ headers is available since Next.js v13.0.0).
```ts filename="proxy.ts" switcher
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-proxy1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-proxy1', 'hello')
// You can also set request headers in NextResponse.next
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-proxy2`
response.headers.set('x-hello-from-proxy2', 'hello')
return response
}
```
```js filename="proxy.js" switcher
import { NextResponse } from 'next/server'
export function proxy(request) {
// Clone the request headers and set a new header `x-hello-from-proxy1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-proxy1', 'hello')
// You can also set request headers in NextResponse.next
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-proxy2`
response.headers.set('x-hello-from-proxy2', 'hello')
return response
}
```
Note that the snippet uses:
- `NextResponse.next({ request: { headers: requestHeaders } })` to make `requestHeaders` available upstream
- **NOT** `NextResponse.next({ headers: requestHeaders })` which makes `requestHeaders` available to clients
Learn more in [NextResponse headers in Proxy](/docs/app/api-reference/functions/next-response#next).
> **Good to know**: Avoid setting large headers as it might cause [431 Request Header Fields Too Large](https://developer.mozilla.org/docs/Web/HTTP/Status/431) error depending on your backend web server configuration.
#### RSC requests and rewrites
During RSC requests, Next.js strips internal Flight headers from the `request` instance in Proxy. For example, headers like `rsc`, `next-router-state-tree`, and `next-router-prefetch` are not exposed through `request.headers`. This is to prevent accidentally handling an RSC request differently than the HTML request as both need to align.
When you use `NextResponse.rewrite()`, Next.js automatically propagates the required RSC rewrite headers upstream.
If you implement custom rewrite logic with `fetch()` instead of `NextResponse.rewrite()`, you can run into missing RSC headers unless you forward them manually.
For custom `fetch` rewrite setups, you can also enable `skipProxyUrlNormalize` in `next.config.js` so your rewrite logic can receive the necessary URL shape and RSC headers from the provided request object:
```js filename="next.config.js"
module.exports = {
skipProxyUrlNormalize: true,
}
```
### CORS
You can set CORS headers in Proxy to allow cross-origin requests, including [simple](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) and [preflighted](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests) requests.
```tsx filename="proxy.ts" switcher
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function proxy(request: NextRequest) {
// Check the origin from the request
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Handle preflighted requests
const isPreflight = request.method === 'OPTIONS'
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// Handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
export const config = {
matcher: '/api/:path*',
}
```
```jsx filename="proxy.js" switcher
import { NextResponse } from 'next/server'
const allowedOrigins = ['https://acme.com', 'https://my-app.org']
const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}
export function proxy(request) {
// Check the origin from the request
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)
// Handle preflighted requests
const isPreflight = request.method === 'OPTIONS'
if (isPreflight) {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}
// Handle simple requests
const response = NextResponse.next()
if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}
Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})
return response
}
export const config = {
matcher: '/api/:path*',
}
```
<AppOnly>
> **Good to know:** You can configure CORS headers for individual routes in [Route Handlers](/docs/app/api-reference/file-conventions/route#cors).
</AppOnly>
### Producing a response
You can respond from Proxy directly by returning a `Response` or `NextResponse` instance. (This is available since [Next.js v13.1.0](https://nextjs.org/blog/next-13-1#nextjs-advanced-proxy))
```ts filename="proxy.ts" switcher
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the proxy to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function proxy(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
```
```js filename="proxy.js" switcher
import { isAuthenticated } from '@lib/auth'
// Limit the proxy to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function proxy(request) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
```
### Negative matching
The `matcher` config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here:
```js filename="proxy.js"
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
],
}
```
You can also bypass Proxy for certain requests by using the `missing` or `has` arrays, or a combination of both:
```js filename="proxy.js"
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
*/
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
has: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
{
source:
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
has: [{ type: 'header', key: 'x-present' }],
missing: [{ type: 'header', key: 'x-missing', value: 'prefetch' }],
},
],
}
```
> **Good to know**:
>
> Even when `_next/data` is excluded in a negative matcher pattern, proxy will still be invoked for `_next/data` routes. This is intentional behavior to prevent accidental security issues where you might protect a page but forget to protect the corresponding data route.
```js filename="proxy.js"
export const config = {
matcher:
'/((?!api|_next/data|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)',
}
// Proxy will still run for /_next/data/* routes despite being excluded
```
### `waitUntil` and `NextFetchEvent`
The `NextFetchEvent` object extends the native [`FetchEvent`](https://developer.mozilla.org/docs/Web/API/FetchEvent) object, and includes the [`waitUntil()`](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) method.
The `waitUntil()` method takes a promise as an argument, and extends the lifetime of the Proxy until the promise settles. This is useful for performing work in the background.
```ts filename="proxy.ts"
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'
export function proxy(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
method: 'POST',
body: JSON.stringify({ pathname: req.nextUrl.pathname }),
})
)
return NextResponse.next()
}
```
### Unit testing (experimental)
Starting in Next.js 15.1, the `next/experimental/testing/server` package contains utilities to help unit test proxy files. Unit testing proxy can help ensure that it's only run on desired paths and that custom routing logic works as intended before code reaches production.
The `unstable_doesProxyMatch` function can be used to assert whether proxy will run for the provided URL, headers, and cookies.
```js
import { unstable_doesProxyMatch } from 'next/experimental/testing/server'
expect(
unstable_doesProxyMatch({
config,
nextConfig,
url: '/test',
})
).toEqual(false)
```
The entire proxy function can also be tested.
```js
import { isRewrite, getRewrittenUrl } from 'next/experimental/testing/server'
const request = new NextRequest('https://nextjs.org/docs')
const response = await proxy(request)
expect(isRewrite(response)).toEqual(true)
expect(getRewrittenUrl(response)).toEqual('https://other-domain.com/docs')
// getRedirectUrl could also be used if the response were a redirect
```
## Platform support
| Deployment Option | Supported |
| ------------------------------------------------------------------- | ----------------- |
| [Node.js server](/docs/app/getting-started/deploying#nodejs-server) | Yes |
| [Docker container](/docs/app/getting-started/deploying#docker) | Yes |
| [Static export](/docs/app/getting-started/deploying#static-export) | No |
| [Adapters](/docs/app/getting-started/deploying#adapters) | Platform-specific |
Learn how to [configure Proxy](/docs/app/guides/self-hosting#proxy) when self-hosting Next.js.
## Migration to Proxy
### Why the Change
The reason behind the renaming of `middleware` is that the term "middleware" can often be confused with Express.js middleware, leading to a misinterpretation of its purpose. Also, Middleware is highly capable, so it may encourage the usage; however, this feature is recommended to be used as a last resort.
Next.js is moving forward to provide better APIs with better ergonomics so that developers can achieve their goals without Middleware. This is the reason behind the renaming of `middleware`.
### Why "Proxy"
The name Proxy clarifies what Middleware is capable of. The term "proxy" implies that it has a network boundary in front of the app, which is the behavior of Middleware. Also, Middleware defaults to run at the [Edge Runtime](/docs/app/api-reference/edge), which can run closer to the client, separated from the app's region. These behaviors align better with the term "proxy" and provide a clearer purpose of the feature.
### How to Migrate
We recommend users avoid relying on Middleware unless no other options exist. Our goal is to give them APIs with better ergonomics so they can achieve their goals without Middleware.
The term “middleware” often confuses users with Express.js middleware, which can encourage misuse. To clarify our direction, we are renaming the file convention to “proxy.” This highlights that we are moving away from Middleware, breaking down its overloaded features, and making the Proxy clear in its purpose.
Next.js provides a codemod to migrate from `middleware.ts` to `proxy.ts`. You can run the following command to migrate:
```bash
npx @next/codemod@canary middleware-to-proxy .
```
The codemod will rename the file and the function name from `middleware` to `proxy`.
```diff
// middleware.ts -> proxy.ts
- export function middleware() {
+ export function proxy() {
```
## Version history
| Version | Changes |
| --------- | --------------------------------------------------------------------------------------------- |
| `v16.0.0` | Middleware is deprecated and renamed to Proxy |
| `v15.5.0` | Middleware can now use the Node.js runtime (stable) |
| `v15.2.0` | Middleware can now use the Node.js runtime (experimental) |
| `v13.1.0` | Advanced Middleware flags added |
| `v13.0.0` | Middleware can modify request headers, response headers, and send responses |
| `v12.2.0` | Middleware is stable, please see the [upgrade guide](/docs/messages/middleware-upgrade-guide) |
| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) |
| `v12.0.0` | Middleware (Beta) added |
@@ -0,0 +1,45 @@
---
title: public Folder
nav_title: public
description: Next.js allows you to serve static files, like images, in the public directory. You can learn how it works here.
---
{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
Next.js can serve static files, like images, under a folder called `public` in the root directory. Files inside `public` can then be referenced by your code starting from the base URL (`/`).
For example, the file `public/avatars/me.png` can be viewed by visiting the `/avatars/me.png` path. The code to display that image might look like:
```jsx filename="avatar.js"
import Image from 'next/image'
export function Avatar({ id, alt }) {
return <Image src={`/avatars/${id}.png`} alt={alt} width="64" height="64" />
}
export function AvatarOfMe() {
return <Avatar id="me" alt="A portrait of me" />
}
```
## Caching
Next.js cannot safely cache assets in the `public` folder because they may change. The default caching headers applied are:
```jsx
Cache-Control: public, max-age=0
```
## Robots, Favicons, and others
<PagesOnly>
The folder is also useful for `robots.txt`, `favicon.ico`, Google Site Verification, and any other static files (including `.html`). But make sure to not have a static file with the same name as a file in the `pages/` directory, as this will result in an error. [Read more](/docs/messages/conflicting-public-file-page).
</PagesOnly>
<AppOnly>
For static metadata files, such as `robots.txt`, `favicon.ico`, etc, you should use [special metadata files](/docs/app/api-reference/file-conventions/metadata) inside the `app` folder.
</AppOnly>
@@ -0,0 +1,32 @@
---
title: Route Groups
description: Route Groups can be used to partition your Next.js application into different sections.
---
Route Groups are a folder convention that let you organize routes by category or team.
## Convention
A route group can be created by wrapping a folder's name in parenthesis: `(folderName)`.
This convention indicates the folder is for organizational purposes and should **not be included** in the route's URL path.
<Image
alt="An example folder structure using route groups"
srcLight="/docs/light/project-organization-route-groups.png"
srcDark="/docs/dark/project-organization-route-groups.png"
width="1600"
height="849"
/>
## Use cases
- Organizing routes by team, concern, or feature.
- Defining multiple [root layouts](/docs/app/api-reference/file-conventions/layout#root-layout).
- Opting specific route segments into sharing a layout, while keeping others out.
## Caveats
- **Full page load**: If you navigate between routes that use different root layouts, it'll trigger a full page reload. For example, navigating from `/cart` that uses `app/(shop)/layout.js` to `/blog` that uses `app/(marketing)/layout.js`. This **only** applies to multiple root layouts.
- **Conflicting paths**: Routes in different groups should not resolve to the same URL path. For example, `(marketing)/about/page.js` and `(shop)/about/page.js` would both resolve to `/about` and cause an error.
- **Top-level root layout**: If you use multiple root layouts without a top-level `layout.js` file, make sure your home route (/) is defined within one of the route groups, e.g. app/(marketing)/page.js.
@@ -0,0 +1,670 @@
---
title: route.js
description: API reference for the route.js special file.
---
Route Handlers allow you to create custom request handlers for a given route using the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) and [Response](https://developer.mozilla.org/docs/Web/API/Response) APIs.
```ts filename="route.ts" switcher
export async function GET() {
return Response.json({ message: 'Hello World' })
}
```
```js filename="route.js" switcher
export async function GET() {
return Response.json({ message: 'Hello World' })
}
```
## Reference
### HTTP Methods
A **route** file allows you to create custom request handlers for a given route. The following [HTTP methods](https://developer.mozilla.org/docs/Web/HTTP/Methods) are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`.
```ts filename="route.ts" switcher
export async function GET(request: Request) {}
export async function HEAD(request: Request) {}
export async function POST(request: Request) {}
export async function PUT(request: Request) {}
export async function DELETE(request: Request) {}
export async function PATCH(request: Request) {}
// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and set the appropriate Response `Allow` header depending on the other methods defined in the Route Handler.
export async function OPTIONS(request: Request) {}
```
```js filename="route.js" switcher
export async function GET(request) {}
export async function HEAD(request) {}
export async function POST(request) {}
export async function PUT(request) {}
export async function DELETE(request) {}
export async function PATCH(request) {}
// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and set the appropriate Response `Allow` header depending on the other methods defined in the Route Handler.
export async function OPTIONS(request) {}
```
### Parameters
#### `request` (optional)
The `request` object is a [NextRequest](/docs/app/api-reference/functions/next-request) object, which is an extension of the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) API. `NextRequest` gives you further control over the incoming request, including easily accessing `cookies` and an extended, parsed, URL object `nextUrl`.
```ts filename="route.ts" switcher
import type { NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const url = request.nextUrl
}
```
```js filename="route.js" switcher
export async function GET(request) {
const url = request.nextUrl
}
```
#### `context` (optional)
- **`params`**: a promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) for the current route.
```ts filename="app/dashboard/[team]/route.ts" switcher
export async function GET(
request: Request,
{ params }: { params: Promise<{ team: string }> }
) {
const { team } = await params
}
```
```js filename="app/dashboard/[team]/route.js" switcher
export async function GET(request, { params }) {
const { team } = await params
}
```
| Example | URL | `params` |
| -------------------------------- | -------------- | ---------------------------------- |
| `app/dashboard/[team]/route.js` | `/dashboard/1` | `Promise<{ team: '1' }>` |
| `app/shop/[tag]/[item]/route.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` |
| `app/blog/[...slug]/route.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` |
### Route Context Helper
You can type the Route Handler context using `RouteContext` to get strongly typed `params` from a route literal. `RouteContext` is a globally available helper.
```ts filename="app/users/[id]/route.ts"
import type { NextRequest } from 'next/server'
export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) {
const { id } = await ctx.params
return Response.json({ id })
}
```
> **Good to know**
>
> - Types are generated during `next dev`, `next build` or `next typegen`.
> - After type generation, the `RouteContext` helper is globally available. It doesn't need to be imported.
## Examples
### Cookies
You can read or set cookies with [`cookies`](/docs/app/api-reference/functions/cookies) from `next/headers`.
```ts filename="route.ts" switcher
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) {
const cookieStore = await cookies()
const a = cookieStore.get('a')
const b = cookieStore.set('b', '1')
const c = cookieStore.delete('c')
}
```
```js filename="route.js" switcher
import { cookies } from 'next/headers'
export async function GET(request) {
const cookieStore = await cookies()
const a = cookieStore.get('a')
const b = cookieStore.set('b', '1')
const c = cookieStore.delete('c')
}
```
Alternatively, you can return a new `Response` using the [`Set-Cookie`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie) header.
```ts filename="app/api/route.ts" switcher
import { cookies } from 'next/headers'
export async function GET(request: Request) {
const cookieStore = await cookies()
const token = cookieStore.get('token')
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token.value}` },
})
}
```
```js filename="app/api/route.js" switcher
import { cookies } from 'next/headers'
export async function GET(request) {
const cookieStore = await cookies()
const token = cookieStore.get('token')
return new Response('Hello, Next.js!', {
status: 200,
headers: { 'Set-Cookie': `token=${token.value}` },
})
}
```
You can also use the underlying Web APIs to read cookies from the request ([`NextRequest`](/docs/app/api-reference/functions/next-request)):
```ts filename="app/api/route.ts" switcher
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const token = request.cookies.get('token')
}
```
```js filename="app/api/route.js" switcher
export async function GET(request) {
const token = request.cookies.get('token')
}
```
### Headers
You can read headers with [`headers`](/docs/app/api-reference/functions/headers) from `next/headers`.
```ts filename="route.ts" switcher
import { headers } from 'next/headers'
import type { NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const headersList = await headers()
const referer = headersList.get('referer')
}
```
```js filename="route.js" switcher
import { headers } from 'next/headers'
export async function GET(request) {
const headersList = await headers()
const referer = headersList.get('referer')
}
```
This `headers` instance is read-only. To set headers, you need to return a new `Response` with new `headers`.
```ts filename="app/api/route.ts" switcher
import { headers } from 'next/headers'
export async function GET(request: Request) {
const headersList = await headers()
const referer = headersList.get('referer')
return new Response('Hello, Next.js!', {
status: 200,
headers: { referer: referer },
})
}
```
```js filename="app/api/route.js" switcher
import { headers } from 'next/headers'
export async function GET(request) {
const headersList = await headers()
const referer = headersList.get('referer')
return new Response('Hello, Next.js!', {
status: 200,
headers: { referer: referer },
})
}
```
You can also use the underlying Web APIs to read headers from the request ([`NextRequest`](/docs/app/api-reference/functions/next-request)):
```ts filename="app/api/route.ts" switcher
import { type NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
}
```
```js filename="app/api/route.js" switcher
export async function GET(request) {
const requestHeaders = new Headers(request.headers)
}
```
### Revalidating Cached Data
You can [revalidate cached data](/docs/app/guides/incremental-static-regeneration) using the `revalidate` route segment config option.
```ts filename="app/posts/route.ts" switcher
export const revalidate = 60
export async function GET() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return Response.json(posts)
}
```
```js filename="app/posts/route.js" switcher
export const revalidate = 60
export async function GET() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return Response.json(posts)
}
```
### Redirects
```ts filename="app/api/route.ts" switcher
import { redirect } from 'next/navigation'
export async function GET(request: Request) {
redirect('https://nextjs.org/')
}
```
```js filename="app/api/route.js" switcher
import { redirect } from 'next/navigation'
export async function GET(request) {
redirect('https://nextjs.org/')
}
```
### Dynamic Route Segments
Route Handlers can use [Dynamic Segments](/docs/app/api-reference/file-conventions/dynamic-routes) to create request handlers from dynamic data.
```ts filename="app/items/[slug]/route.ts" switcher
export async function GET(
request: Request,
{ params }: { params: Promise<{ slug: string }> }
) {
const { slug } = await params // 'a', 'b', or 'c'
}
```
```js filename="app/items/[slug]/route.js" switcher
export async function GET(request, { params }) {
const { slug } = await params // 'a', 'b', or 'c'
}
```
| Route | Example URL | `params` |
| --------------------------- | ----------- | ------------------------ |
| `app/items/[slug]/route.js` | `/items/a` | `Promise<{ slug: 'a' }>` |
| `app/items/[slug]/route.js` | `/items/b` | `Promise<{ slug: 'b' }>` |
| `app/items/[slug]/route.js` | `/items/c` | `Promise<{ slug: 'c' }>` |
#### Static Generation with `generateStaticParams`
You can use [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) with dynamic Route Handlers to statically generate responses at build time for specified params, while handling other params dynamically at request time.
When using [Cache Components](/docs/app/getting-started/caching), you can combine `generateStaticParams` with `use cache` to enable data caching for both prerendered and runtime params.
See the [generateStaticParams with Route Handlers](/docs/app/api-reference/functions/generate-static-params#with-route-handlers) documentation for examples and details.
### URL Query Parameters
The request object passed to the Route Handler is a `NextRequest` instance, which includes [some additional convenience methods](/docs/app/api-reference/functions/next-request#nexturl), such as those for more easily handling query parameters.
```ts filename="app/api/search/route.ts" switcher
import { type NextRequest } from 'next/server'
export function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
```
```js filename="app/api/search/route.js" switcher
export function GET(request) {
const searchParams = request.nextUrl.searchParams
const query = searchParams.get('query')
// query is "hello" for /api/search?query=hello
}
```
### Streaming
Streaming is commonly used in combination with Large Language Models (LLMs), such as OpenAI, for AI-generated content. Learn more about the [AI SDK](https://sdk.vercel.ai/docs/introduction).
```ts filename="app/api/chat/route.ts" switcher
import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4-turbo'),
messages,
})
return new StreamingTextResponse(result.toAIStream())
}
```
```js filename="app/api/chat/route.js" switcher
import { openai } from '@ai-sdk/openai'
import { StreamingTextResponse, streamText } from 'ai'
export async function POST(req) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4-turbo'),
messages,
})
return new StreamingTextResponse(result.toAIStream())
}
```
These abstractions use the Web APIs to create a stream. You can also use the underlying Web APIs directly.
```ts filename="app/api/route.ts" switcher
// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator: any) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time: number) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
```
```js filename="app/api/route.js" switcher
// https://developer.mozilla.org/docs/Web/API/ReadableStream#convert_async_iterator_to_stream
function iteratorToStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
if (done) {
controller.close()
} else {
controller.enqueue(value)
}
},
})
}
function sleep(time) {
return new Promise((resolve) => {
setTimeout(resolve, time)
})
}
const encoder = new TextEncoder()
async function* makeIterator() {
yield encoder.encode('<p>One</p>')
await sleep(200)
yield encoder.encode('<p>Two</p>')
await sleep(200)
yield encoder.encode('<p>Three</p>')
}
export async function GET() {
const iterator = makeIterator()
const stream = iteratorToStream(iterator)
return new Response(stream)
}
```
### Request Body
You can read the `Request` body using the standard Web API methods:
```ts filename="app/items/route.ts" switcher
export async function POST(request: Request) {
const res = await request.json()
return Response.json({ res })
}
```
```js filename="app/items/route.js" switcher
export async function POST(request) {
const res = await request.json()
return Response.json({ res })
}
```
### Request Body FormData
You can read the `FormData` using the `request.formData()` function:
```ts filename="app/items/route.ts" switcher
export async function POST(request: Request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
```
```js filename="app/items/route.js" switcher
export async function POST(request) {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
return Response.json({ name, email })
}
```
Since `formData` data are all strings, you may want to use [`zod-form-data`](https://www.npmjs.com/zod-form-data) to validate the request and retrieve data in the format you prefer (e.g. `number`).
### CORS
You can set CORS headers for a specific Route Handler using the standard Web API methods:
```ts filename="app/api/route.ts" switcher
export async function GET(request: Request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
```
```js filename="app/api/route.js" switcher
export async function GET(request) {
return new Response('Hello, Next.js!', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
},
})
}
```
> **Good to know**:
>
> - To add CORS headers to multiple Route Handlers, you can use [Proxy](/docs/app/api-reference/file-conventions/proxy#cors) or the [`next.config.js` file](/docs/app/api-reference/config/next-config-js/headers#cors).
### Webhooks
You can use a Route Handler to receive webhooks from third-party services:
```ts filename="app/api/route.ts" switcher
export async function POST(request: Request) {
try {
const text = await request.text()
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
})
}
return new Response('Success!', {
status: 200,
})
}
```
```js filename="app/api/route.js" switcher
export async function POST(request) {
try {
const text = await request.text()
// Process the webhook payload
} catch (error) {
return new Response(`Webhook error: ${error.message}`, {
status: 400,
})
}
return new Response('Success!', {
status: 200,
})
}
```
Notably, unlike API Routes with the Pages Router, you do not need to use `bodyParser` to use any additional configuration.
### Non-UI Responses
You can use Route Handlers to return non-UI content. Note that [`sitemap.xml`](/docs/app/api-reference/file-conventions/metadata/sitemap#generating-a-sitemap-using-code-js-ts), [`robots.txt`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file), [`app icons`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx), and [open graph images](/docs/app/api-reference/file-conventions/metadata/opengraph-image) all have built-in support.
```ts filename="app/rss.xml/route.ts" switcher
export async function GET() {
return new Response(
`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Next.js Documentation</title>
<link>https://nextjs.org/docs</link>
<description>The React Framework for the Web</description>
</channel>
</rss>`,
{
headers: {
'Content-Type': 'text/xml',
},
}
)
}
```
```js filename="app/rss.xml/route.js" switcher
export async function GET() {
return new Response(`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>Next.js Documentation</title>
<link>https://nextjs.org/docs</link>
<description>The React Framework for the Web</description>
</channel>
</rss>`)
}
```
### Segment Config Options
Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as pages and layouts.
```ts filename="app/items/route.ts" switcher
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
```
```js filename="app/items/route.js" switcher
export const dynamic = 'auto'
export const dynamicParams = true
export const revalidate = false
export const fetchCache = 'auto'
export const runtime = 'nodejs'
export const preferredRegion = 'auto'
```
See the [API reference](/docs/app/api-reference/file-conventions/route-segment-config) for more details.
## Version History
| Version | Changes |
| ------------ | ---------------------------------------------------------------------------------------------------- |
| `v15.0.0-RC` | `context.params` is now a promise. A [codemod](/docs/app/guides/upgrading/codemods#150) is available |
| `v15.0.0-RC` | The default caching for `GET` handlers was changed from static to dynamic |
| `v13.2.0` | Route Handlers are introduced. |
@@ -0,0 +1,35 @@
---
title: src Folder
nav_title: src
description: Save pages under the `src` folder as an alternative to the root `pages` directory.
related:
links:
- app/getting-started/project-structure
---
{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
As an alternative to having the special Next.js `app` or `pages` directories in the root of your project, Next.js also supports the common pattern of placing application code under the `src` folder.
This separates application code from project configuration files which mostly live in the root of a project, which is preferred by some individuals and teams.
To use the `src` folder, move the `app` Router folder or `pages` Router folder to `src/app` or `src/pages` respectively.
<Image
alt="An example folder structure with the `src` folder"
srcLight="/docs/light/project-organization-src-directory.png"
srcDark="/docs/dark/project-organization-src-directory.png"
width="1600"
height="687"
/>
> **Good to know**:
>
> - The `/public` directory should remain in the root of your project.
> - Config files like `package.json`, `next.config.js` and `tsconfig.json` should remain in the root of your project.
> - `.env.*` files should remain in the root of your project.
> - `src/app` or `src/pages` will be ignored if `app` or `pages` are present in the root directory.
> - If you're using `src`, you'll probably also move other application folders such as `/components` or `/lib`.
> - If you're using Proxy, ensure it is placed inside the `src` folder.
> - If you're using Tailwind CSS, you'll need to add the `/src` prefix to the `tailwind.config.js` file in the [content section](https://tailwindcss.com/docs/content-configuration).
> - If you are using TypeScript paths for imports such as `@/*`, you should update the `paths` object in `tsconfig.json` to include `src/`.
@@ -0,0 +1,162 @@
---
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. |
@@ -0,0 +1,114 @@
---
title: unauthorized.js
description: API reference for the unauthorized.js special file.
related:
links:
- app/api-reference/functions/unauthorized
version: experimental
---
The **unauthorized** file is used to render UI when the [`unauthorized`](/docs/app/api-reference/functions/unauthorized) function is invoked during authentication. Along with allowing you to customize the UI, Next.js will return a `401` status code.
```tsx filename="app/unauthorized.tsx" switcher
import Login from '@/app/components/Login'
export default function Unauthorized() {
return (
<main>
<h1>401 - Unauthorized</h1>
<p>Please log in to access this page.</p>
<Login />
</main>
)
}
```
```jsx filename="app/unauthorized.js" switcher
import Login from '@/app/components/Login'
export default function Unauthorized() {
return (
<main>
<h1>401 - Unauthorized</h1>
<p>Please log in to access this page.</p>
<Login />
</main>
)
}
```
## Reference
### Props
`unauthorized.js` components do not accept any props.
## Examples
### Displaying login UI to unauthenticated users
You can use [`unauthorized`](/docs/app/api-reference/functions/unauthorized) function to render the `unauthorized.js` file with a login UI.
```tsx filename="app/dashboard/page.tsx" switcher
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
export default async function DashboardPage() {
const session = await verifySession()
if (!session) {
unauthorized()
}
return <div>Dashboard</div>
}
```
```jsx filename="app/dashboard/page.js" switcher
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
export default async function DashboardPage() {
const session = await verifySession()
if (!session) {
unauthorized()
}
return <div>Dashboard</div>
}
```
```tsx filename="app/unauthorized.tsx" switcher
import Login from '@/app/components/Login'
export default function UnauthorizedPage() {
return (
<main>
<h1>401 - Unauthorized</h1>
<p>Please log in to access this page.</p>
<Login />
</main>
)
}
```
```jsx filename="app/unauthorized.js" switcher
import Login from '@/app/components/Login'
export default function UnauthorizedPage() {
return (
<main>
<h1>401 - Unauthorized</h1>
<p>Please log in to access this page.</p>
<Login />
</main>
)
}
```
## Version History
| Version | Changes |
| --------- | ----------------------------- |
| `v15.1.0` | `unauthorized.js` introduced. |