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,303 @@
---
title: after
description: API Reference for the after function.
---
`after` allows you to schedule work to be executed after a response (or prerender) is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics.
It can be used in [Server Components](/docs/app/getting-started/server-and-client-components) (including [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata)), [Server Functions](/docs/app/getting-started/mutating-data), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Proxy](/docs/app/api-reference/file-conventions/proxy).
The function accepts a callback that will be executed after the response (or prerender) is finished:
```tsx filename="app/layout.tsx" switcher
import { after } from 'next/server'
// Custom logging function
import { log } from '@/app/utils'
export default function Layout({ children }: { children: React.ReactNode }) {
after(() => {
// Execute after the layout is rendered and sent to the user
log()
})
return <>{children}</>
}
```
```jsx filename="app/layout.jsx" switcher
import { after } from 'next/server'
// Custom logging function
import { log } from '@/app/utils'
export default function Layout({ children }) {
after(() => {
// Execute after the layout is rendered and sent to the user
log()
})
return <>{children}</>
}
```
> **Good to know:** `after` is not a [Request-time API](/docs/app/glossary#request-time-apis) and calling it does not cause a route to become dynamic. If it's used within a static page, the callback will execute at build time, or whenever a page is revalidated.
## Reference
### Parameters
- A callback function which will be executed after the response (or prerender) is finished.
### Duration
`after` will run for the platform's default or configured max duration of your route. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config/maxDuration) route segment config.
## Good to know
- `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called.
- You can use React `cache` to deduplicate functions called inside `after`.
- `after` can be nested inside other `after` calls, for example, you can create utility functions that wrap `after` calls to add additional functionality.
## Examples
### With request APIs
Whether you can use request APIs like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers) inside `after` depends on where `after` is called from.
#### In Route Handlers and Server Functions
You can call `cookies` and `headers` directly inside the `after` callback when used in [Route Handlers](/docs/app/api-reference/file-conventions/route) and [Server Functions](/docs/app/getting-started/mutating-data). This is useful for logging activity after a mutation or API request. For example:
```ts filename="app/api/route.ts" highlight={2,10-16} switcher
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'
export async function POST(request: Request) {
// Perform mutation
// ...
// Log user activity for analytics
after(async () => {
const userAgent = (await headers()).get('user-agent') || 'unknown'
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
logUserAction({ sessionCookie, userAgent })
})
return new Response(JSON.stringify({ status: 'success' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}
```
```js filename="app/api/route.js" highlight={2,10-16} switcher
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'
export async function POST(request) {
// Perform mutation
// ...
// Log user activity for analytics
after(async () => {
const userAgent = (await headers()).get('user-agent') || 'unknown'
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
logUserAction({ sessionCookie, userAgent })
})
return new Response(JSON.stringify({ status: 'success' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}
```
#### In Server Components (pages and layouts)
[Server Components](/docs/app/getting-started/server-and-client-components) (including pages, layouts, and `generateMetadata`) **cannot** use `cookies`, `headers`, or other Request-time APIs inside `after`. This is because Next.js needs to know which part of the component tree accesses request data to support [Partial Prerendering](/docs/app/glossary#partial-prerendering-ppr) and [Cache Components](/docs/app/getting-started/caching), but `after` runs after React's rendering lifecycle.
If you need request data inside an `after` callback in a Server Component, read it beforehand and pass the values in:
```tsx filename="app/page.tsx" highlight={8-10,12} switcher
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'
export default async function Page() {
// Read request data before `after` — this is allowed
// These calls will be read during the component's rendering lifecycle
const userAgent = (await headers()).get('user-agent') || 'unknown'
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
after(() => {
// Use the values read above
logUserAction({ sessionCookie, userAgent })
})
return <h1>My Page</h1>
}
```
```jsx filename="app/page.jsx" highlight={8-10,12} switcher
import { after } from 'next/server'
import { cookies, headers } from 'next/headers'
import { logUserAction } from '@/app/utils'
export default async function Page() {
// Read request data before `after` — this is allowed
// These calls will be read during the component's rendering lifecycle
const userAgent = (await headers()).get('user-agent') || 'unknown'
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
after(() => {
// Use the values read above
logUserAction({ sessionCookie, userAgent })
})
return <h1>My Page</h1>
}
```
Calling `cookies()` or `headers()` inside the `after` callback in a Server Component will throw a runtime error.
#### With Cache Components
When using [Cache Components](/docs/app/getting-started/caching), components that access request data like `cookies` or `headers` must be wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the rest of the page can be prerendered into a static shell.
You can combine this pattern with `after` by reading request data in a dynamic component and passing it into `after`:
```tsx filename="app/page.tsx" highlight={18-19,22-24} switcher
import { Suspense } from 'react'
import { after } from 'next/server'
import { cookies } from 'next/headers'
import { logUserAction } from '@/app/utils'
export default function Page() {
return (
<>
<h1>Part of the static shell</h1>
<Suspense fallback={<p>Loading...</p>}>
<DynamicContent />
</Suspense>
</>
)
}
async function DynamicContent() {
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
// Schedule work after the response is sent
after(() => {
logUserAction({ sessionCookie })
})
return <p>Your session: {sessionCookie}</p>
}
```
```jsx filename="app/page.jsx" highlight={18-19,22-24} switcher
import { Suspense } from 'react'
import { after } from 'next/server'
import { cookies } from 'next/headers'
import { logUserAction } from '@/app/utils'
export default function Page() {
return (
<>
<h1>Part of the static shell</h1>
<Suspense fallback={<p>Loading...</p>}>
<DynamicContent />
</Suspense>
</>
)
}
async function DynamicContent() {
const sessionCookie =
(await cookies()).get('session-id')?.value || 'anonymous'
// Schedule work after the response is sent
after(() => {
logUserAction({ sessionCookie })
})
return <p>Your session: {sessionCookie}</p>
}
```
In this example, `<h1>` and the `<Suspense>` fallback are included in the static shell. `DynamicContent` reads the cookie during rendering and passes it into `after` via closure. Since `cookies()` is called _outside_ the `after` callback (during the component's render), this works correctly.
## 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 `after`](/docs/app/guides/self-hosting#after) when self-hosting Next.js.
<details id="after-serverless">
<summary>Reference: supporting `after` for serverless platforms</summary>
Using `after` in a serverless context requires waiting for asynchronous tasks to finish after the response has been sent. In Next.js and Vercel, this is achieved using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation until all promises passed to [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference#waituntil) have settled.
If you want your users to be able to run `after`, you will have to provide your implementation of `waitUntil` that behaves in an analogous way.
When `after` is called, Next.js will access `waitUntil` like this:
```jsx
const RequestContext = globalThis[Symbol.for('@next/request-context')]
const contextValue = RequestContext?.get()
const waitUntil = contextValue?.waitUntil
```
Which means that `globalThis[Symbol.for('@next/request-context')]` is expected to contain an object like this:
```tsx
type NextRequestContext = {
get(): NextRequestContextValue | undefined
}
type NextRequestContextValue = {
waitUntil?: (promise: Promise<any>) => void
}
```
Here is an example of the implementation.
```tsx
import { AsyncLocalStorage } from 'node:async_hooks'
const RequestContextStorage = new AsyncLocalStorage<NextRequestContextValue>()
// Define and inject the accessor that next.js will use
const RequestContext: NextRequestContext = {
get() {
return RequestContextStorage.getStore()
},
}
globalThis[Symbol.for('@next/request-context')] = RequestContext
const handler = (req, res) => {
const contextValue = { waitUntil: YOUR_WAITUNTIL }
// Provide the value
return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res))
}
```
</details>
## Version History
| Version | Changes |
| ------------ | ---------------------------- |
| `v15.1.0` | `after` became stable. |
| `v15.0.0-rc` | `unstable_after` introduced. |
@@ -0,0 +1,584 @@
---
title: cacheLife
description: Learn how to use the cacheLife function to set the cache expiration time for a cached function or component.
related:
title: Related
description: View related API references.
links:
- app/api-reference/config/next-config-js/cacheComponents
- app/api-reference/directives/use-cache
- app/api-reference/functions/revalidateTag
- app/api-reference/functions/cacheTag
---
The `cacheLife` function is used to set the cache lifetime of a function or component. It should be used alongside the [`use cache`](/docs/app/api-reference/directives/use-cache) directive, and within the scope of the function or component.
## Usage
### Basic setup
To use `cacheLife`, first enable the [`cacheComponents` flag](/docs/app/api-reference/config/next-config-js/cacheComponents) in your `next.config.js` file:
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
```
```js filename="next.config.js" switcher
const nextConfig = {
cacheComponents: true,
}
export default nextConfig
```
`cacheLife` requires the `use cache` directive, which must be placed at the file level or at the top of an async function or component.
> **Good to know**:
>
> - If used, `cacheLife` should be placed within the function whose output is being cached, even when the `use cache` directive is at file level
> - Only one `cacheLife` call should execute per function invocation. You can call `cacheLife` in different control flow branches, but ensure only one executes per run. See the [conditional cache lifetimes](#conditional-cache-lifetimes) example
### Using preset profiles
Next.js provides preset cache profiles that cover common caching needs. Each profile balances three factors:
- How long users see cached content without checking for updates (client-side)
- How often fresh content is generated on the server
- When old content expires completely
Choose a profile based on how frequently your content changes:
- **`seconds`** - Real-time data (stock prices, live scores)
- **`minutes`** - Frequently updated (social feeds, news)
- **`hours`** - Multiple daily updates (product inventory, weather)
- **`days`** - Daily updates (blog posts, articles)
- **`weeks`** - Weekly updates (podcasts, newsletters)
- **`max`** - Rarely changes (legal pages, archived content)
Import `cacheLife` and pass a profile name:
```tsx filename="app/blog/page.tsx" highlight={1,5}
'use cache'
import { cacheLife } from 'next/cache'
export default async function BlogPage() {
cacheLife('days') // Blog content updated daily
const posts = await getBlogPosts()
return <div>{/* render posts */}</div>
}
```
The profile name tells Next.js how to cache the entire function's output. If you don't call `cacheLife`, the `default` profile is used. See [preset cache profiles](#preset-cache-profiles) for timing details.
## Reference
### Cache profile properties
Cache profiles control caching behavior through three timing properties:
- **[`stale`](#stale)**: How long the client can use cached data without checking the server
- **[`revalidate`](#revalidate)**: After this time, the next request will trigger a background refresh
- **[`expire`](#expire)**: After this time with no requests, the next one waits for fresh content
#### `stale`
**Client-side:** How long the client can use cached data without checking the server.
During this time, the client-side router displays cached content immediately without any network request. After this period expires, the router must check with the server on the next navigation or request. This provides instant page loads from the client cache, but data may be outdated.
- If omitted, defaults to the `default` profile's `stale` value (5 minutes, see [`staleTimes`](/docs/app/api-reference/config/next-config-js/staleTimes))
```tsx
cacheLife({ stale: 300 }) // 5 minutes
```
#### `revalidate`
How often the server regenerates cached content in the background.
- When a request arrives after this period, the server:
1. Serves the cached version immediately (if available)
2. Regenerates content in the background
3. Updates the cache with fresh content
- Similar to [Incremental Static Regeneration (ISR)](/docs/app/guides/incremental-static-regeneration)
- If omitted, defaults to the `default` profile's `revalidate` value (15 minutes)
```tsx
cacheLife({ revalidate: 900 }) // 15 minutes
```
#### `expire`
Maximum time before the server must regenerate cached content.
- After this period with no traffic, the server regenerates content synchronously on the next request
- When you set both `revalidate` and `expire`, `expire` must be longer than `revalidate`. Next.js validates this and raises an error for invalid configurations.
- If omitted, defaults to the `default` profile's `expire` value (never expires)
```tsx
cacheLife({ expire: 3600 }) // 1 hour
```
### Preset cache profiles
If you don't specify a profile, Next.js uses the `default` profile. We recommend explicitly setting a profile to make caching behavior clear.
| **Profile** | **Use Case** | `stale` | `revalidate` | `expire` |
| ----------- | -------------------------------------- | ---------- | ------------ | -------- |
| `default` | Standard content | 5 minutes | 15 minutes | never |
| `seconds` | Real-time data | 30 seconds | 1 second | 1 minute |
| `minutes` | Frequently updated content | 5 minutes | 1 minute | 1 hour |
| `hours` | Content updated multiple times per day | 5 minutes | 1 hour | 1 day |
| `days` | Content updated daily | 5 minutes | 1 day | 1 week |
| `weeks` | Content updated weekly | 5 minutes | 1 week | 30 days |
| `max` | Stable content that rarely changes | 5 minutes | 30 days | 1 year |
### Custom cache profiles
Define reusable cache profiles in your `next.config.ts` file:
```ts filename="next.config.ts"
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
biweekly: {
stale: 60 * 60 * 24 * 14, // 14 days
revalidate: 60 * 60 * 24, // 1 day
expire: 60 * 60 * 24 * 14, // 14 days
},
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
const nextConfig = {
cacheComponents: true,
cacheLife: {
biweekly: {
stale: 60 * 60 * 24 * 14, // 14 days
revalidate: 60 * 60 * 24, // 1 day
expire: 60 * 60 * 24 * 14, // 14 days
},
},
}
module.exports = nextConfig
```
The example above caches for 14 days, checks for updates daily, and expires the cache after 14 days. You can then reference this profile throughout your application by its name:
> **Good to know**: Any omitted properties in a custom profile inherit from the `default` profile. This also applies to inline profile objects passed directly to `cacheLife()`.
```tsx filename="app/page.tsx" highlight={5}
'use cache'
import { cacheLife } from 'next/cache'
export default async function Page() {
cacheLife('biweekly')
return <div>Page</div>
}
```
### Overriding the default cache profiles
While the default cache profiles provide a useful way to think about how fresh or stale any given part of cacheable output can be, you may prefer different named profiles to better align with your applications caching strategies.
You can override the default named cache profiles by creating a new configuration with the same name as the defaults.
The example below shows how to override the default `"days"` cache profile:
```ts filename="next.config.ts"
const nextConfig = {
cacheComponents: true,
cacheLife: {
// Override the 'days' profile
days: {
stale: 3600, // 1 hour
revalidate: 900, // 15 minutes
expire: 86400, // 1 day
},
},
}
export default nextConfig
```
### Inline cache profiles
For one-off cases, pass a profile object directly to `cacheLife`:
```tsx filename="app/page.tsx"
'use cache'
import { cacheLife } from 'next/cache'
export default async function Page() {
cacheLife({
stale: 3600,
revalidate: 900,
expire: 86400,
})
return <div>Page</div>
}
```
Inline profiles apply only to the specific function or component. For reusable configurations, define custom profiles in `next.config.ts`.
Using `cacheLife({})` with an empty object applies the `default` profile values.
### Client cache behavior
The `stale` property controls the [Client Cache](/docs/app/glossary#client-cache), not the `Cache-Control` header:
- The server sends the stale time via the `x-nextjs-stale-time` response header
- The client router uses this value to determine when to revalidate
- **Minimum of 30 seconds is enforced** to ensure prefetched links remain usable
This 30-second minimum prevents prefetched data from expiring before users can click on links. It only applies to time-based expiration.
When you call revalidation functions from a Server Action ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag), [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath), [`updateTag`](/docs/app/api-reference/functions/updateTag), or [`refresh`](/docs/app/api-reference/functions/refresh)), the entire client cache is immediately cleared, bypassing the stale time.
> **Good to know**: The `stale` property in `cacheLife` differs from [`staleTimes`](/docs/app/api-reference/config/next-config-js/staleTimes). While `staleTimes` is a global setting affecting all routes, `cacheLife` allows per-function or per-route configuration. Updating `staleTimes.static` also updates the `stale` value of the `default` cache profile.
### Prerendering behavior
Caches with very short lifetimes — zero `revalidate` or `expire` under 5 minutes — are automatically excluded from prerenders and become "dynamic holes" instead. This includes the `seconds` profile.
This behavior allows you to mix static and dynamic content within the same page. Static parts are prerendered, while short-lived caches create boundaries where data is fetched at request time rather than build time. Use a `<Suspense>` boundary around dynamic caches to provide a fallback while content loads.
## Examples
### Using preset profiles
The simplest way to configure caching is using preset profiles. Choose one that matches your content's update pattern:
```tsx filename="app/blog/[slug]/page.tsx"
import { cacheLife } from 'next/cache'
export default async function BlogPost() {
'use cache'
cacheLife('days') // Blog posts updated daily
const post = await fetchBlogPost()
return <article>{post.content}</article>
}
```
```tsx filename="app/products/[id]/page.tsx"
import { cacheLife } from 'next/cache'
export default async function ProductPage() {
'use cache'
cacheLife('hours') // Product data updated multiple times per day
const product = await fetchProduct()
return <div>{product.name}</div>
}
```
### Custom profiles for specific needs
Define custom profiles when preset options don't match your requirements:
```ts filename="next.config.ts"
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {
editorial: {
stale: 600, // 10 minutes
revalidate: 3600, // 1 hour
expire: 86400, // 1 day
},
marketing: {
stale: 300, // 5 minutes
revalidate: 1800, // 30 minutes
expire: 43200, // 12 hours
},
},
}
export default nextConfig
```
Then use these profiles throughout your application:
```tsx filename="app/editorial/page.tsx"
import { cacheLife } from 'next/cache'
export default async function EditorialPage() {
'use cache'
cacheLife('editorial')
// ...
}
```
### Inline profiles for unique cases
Use inline profiles when a specific function needs one-off caching behavior:
```tsx filename="app/api/limited-offer/route.ts"
import { cacheLife } from 'next/cache'
import { getDb } from '@lib/db'
async function getLimitedOffer() {
'use cache'
cacheLife({
stale: 60, // 1 minute
revalidate: 300, // 5 minutes
expire: 3600, // 1 hour
})
const offer = await getDb().offer.findFirst({
where: { type: 'limited' },
orderBy: { created_at: 'desc' },
})
return offer
}
export async function GET() {
const offer = await getLimitedOffer()
return Response.json(offer)
}
```
### Caching individual functions
Apply caching to utility functions for granular control:
```tsx filename="lib/api.ts"
import { cacheLife } from 'next/cache'
export async function getSettings() {
'use cache'
cacheLife('max') // Settings rarely change
return await fetchSettings()
}
```
```tsx filename="lib/stats.ts"
import { cacheLife } from 'next/cache'
export async function getRealtimeStats() {
'use cache'
cacheLife('seconds') // Stats update constantly
return await fetchStats()
}
```
### Nested caching behavior
When you nest `use cache` directives (a cached function or component using another cached function or component), the outer cache's behavior depends on whether it has an explicit `cacheLife`.
#### With explicit outer cacheLife
The outer cache uses its own lifetime, regardless of inner cache lifetimes. When the outer cache hits, it returns the complete output including all nested data. An explicit `cacheLife` always takes precedence, whether it's longer or shorter than inner lifetimes.
```tsx filename="app/dashboard/page.tsx"
import { cacheLife } from 'next/cache'
import { Widget } from './widget'
export default async function Dashboard() {
'use cache'
cacheLife('hours') // Outer scope sets its own lifetime
return (
<div>
<h1>Dashboard</h1>
<Widget /> {/* Inner scope has 'minutes' lifetime */}
</div>
)
}
```
#### Without explicit outer cacheLife
If you don't call `cacheLife` in the outer cache, it uses the `default` profile (15 min revalidate). Inner caches with shorter lifetimes can reduce the outer cache's `default` lifetime. Inner caches with longer lifetimes cannot extend it beyond the default.
```tsx filename="app/dashboard/page.tsx"
import { Widget } from './widget'
export default async function Dashboard() {
'use cache'
// No cacheLife call - uses default (15 min)
// If Widget has 5 min → Dashboard becomes 5 min
// If Widget has 1 hour → Dashboard stays 15 min
return (
<div>
<h1>Dashboard</h1>
<Widget />
</div>
)
}
```
**It is recommended to specify an explicit `cacheLife`.** With explicit lifetime values, you can inspect a cached function or component and immediately know its behavior without tracing through nested caches. Without explicit lifetime values, the behavior becomes dependent on inner cache lifetimes, making it harder to reason about.
#### Nested short-lived caches
As described in [Prerendering behavior](#prerendering-behavior), short-lived caches (zero `revalidate` or `expire` under 5 minutes) become dynamic holes excluded from prerenders.
When a short-lived cache is nested inside another `use cache` without an explicit `cacheLife`, the outer cache's lifetime would silently become short too via propagation. To prevent this accidental misconfiguration, Next.js throws an error during prerendering.
Note that the nested cache may not be obvious — it could be in an imported module or even a third-party dependency:
```tsx filename="components/short-lived-widget.tsx" highlight={5}
import { cacheLife } from 'next/cache'
export async function ShortLivedWidget() {
'use cache'
cacheLife('seconds')
const data = await fetchRealtimeData()
return <div>{data}</div>
}
```
Using this component from another `use cache` without an explicit `cacheLife` will error during prerendering:
```tsx filename="app/page.tsx"
import { ShortLivedWidget } from '@/components/short-lived-widget'
export default async function Page() {
'use cache'
// Error: no explicit cacheLife on outer cache
return (
<div>
<h1>Dashboard</h1>
<p>Last updated: {new Date().toISOString()}</p>
<ShortLivedWidget />
</div>
)
}
```
To fix the error, add an explicit `cacheLife()` to the outer `use cache`:
**If you want the outer cache to remain static (prerendered)**, set a longer cache lifetime:
```tsx filename="app/page.tsx" highlight={6}
import { cacheLife } from 'next/cache'
import { ShortLivedWidget } from '@/components/short-lived-widget'
export default async function Page() {
'use cache'
cacheLife('default') // Explicit cacheLife prevents the error
return (
<div>
<h1>Dashboard</h1>
<p>Last updated: {new Date().toISOString()}</p>
<ShortLivedWidget />
</div>
)
}
```
**If you want the outer cache to also be short-lived**, explicitly set a short cache lifetime to confirm this is intentional. Wrap the component in a `<Suspense>` boundary to provide a fallback while content loads:
```tsx filename="app/page.tsx" highlight={7,17-19}
import { Suspense } from 'react'
import { cacheLife } from 'next/cache'
import { ShortLivedWidget } from '@/components/short-lived-widget'
async function Content() {
'use cache: remote'
cacheLife('seconds') // Explicit cacheLife confirms this is intentionally short-lived
return (
<>
<p>Last updated: {new Date().toISOString()}</p>
<ShortLivedWidget />
</>
)
}
export default function Page() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
</div>
)
}
```
> **Note:** This example uses `"use cache: remote"` because runtime caching in serverless deployments doesn't persist across requests with the default in-memory cache. For self-hosted environments, `"use cache"` may be sufficient. See [Runtime caching considerations](/docs/app/api-reference/directives/use-cache#runtime-caching-considerations) for more details.
### Conditional cache lifetimes
You can call `cacheLife` conditionally in different code paths to set different cache durations based on your application logic:
```tsx filename="lib/posts.ts" highlight={14,19}
import { cacheLife, cacheTag } from 'next/cache'
async function getPostContent(slug: string) {
'use cache'
const post = await fetchPost(slug)
// Tag the cache entry for targeted revalidation
cacheTag(`post-${slug}`)
if (!post) {
// Content may not be published yet or could be in draft
// Cache briefly to reduce database load
cacheLife('minutes')
return null
}
// Published content can be cached longer
cacheLife('days')
// Return only the necessary data to keep cache size minimal
return post.data
}
```
This pattern is useful when different outcomes need different cache durations, for example, when an item is missing but is likely to be available later.
#### Using dynamic cache lifetimes from data
If you want to calculate cache lifetime at runtime, for example by reading it from the fetched data, use an [inline cache profile](#inline-cache-profiles) object:
```tsx filename="lib/posts.ts" highlight={15,16,17,18}
import { cacheLife, cacheTag } from 'next/cache'
async function getPostContent(slug: string) {
'use cache'
const post = await fetchPost(slug)
cacheTag(`post-${slug}`)
if (!post) {
cacheLife('minutes')
return null
}
// Use cache timing from CMS data directly as an object
cacheLife({
// Ensure post.revalidateSeconds is a number in seconds
// stale and expire inherit from 'default' profile
revalidate: post.revalidateSeconds ?? 3600,
})
return post.data
}
```
@@ -0,0 +1,199 @@
---
title: cacheTag
description: Learn how to use the cacheTag function to manage cache invalidation in your Next.js application.
related:
title: Related
description: View related API references.
links:
- app/api-reference/config/next-config-js/cacheComponents
- app/api-reference/directives/use-cache
- app/api-reference/functions/revalidateTag
- app/api-reference/functions/cacheLife
---
The `cacheTag` function allows you to tag cached data for on-demand invalidation. By associating tags with cache entries, you can selectively purge or revalidate specific cache entries without affecting other cached data.
## Usage
To use `cacheTag`, enable the [`cacheComponents` flag](/docs/app/api-reference/config/next-config-js/cacheComponents) in your `next.config.js` file:
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
```
```js filename="next.config.js" switcher
const nextConfig = {
cacheComponents: true,
}
export default nextConfig
```
The `cacheTag` function takes one or more string values.
```tsx filename="app/data.ts" switcher
import { cacheTag } from 'next/cache'
export async function getData() {
'use cache'
cacheTag('my-data')
const data = await fetch('/api/data')
return data
}
```
```jsx filename="app/data.js" switcher
import { cacheTag } from 'next/cache'
export async function getData() {
'use cache'
cacheTag('my-data')
const data = await fetch('/api/data')
return data
}
```
You can then purge the cache on-demand using [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) API in another function, for example, a [route handler](/docs/app/api-reference/file-conventions/route) or [Server Action](/docs/app/getting-started/mutating-data):
```tsx filename="app/action.ts" switcher
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('my-data')
}
```
```jsx filename="app/action.js" switcher
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('my-data')
}
```
## Good to know
- **Idempotent Tags**: Applying the same tag multiple times has no additional effect.
- **Multiple Tags**: You can assign multiple tags to a single cache entry by passing multiple string values to `cacheTag`.
```tsx
cacheTag('tag-one', 'tag-two')
```
- **Limits**: The max length for a custom tag is 256 characters and the max tag items is 128.
## Examples
### Tagging components or functions
Tag your cached data by calling `cacheTag` within a cached function or component:
```tsx filename="app/components/bookings.tsx" switcher
import { cacheTag } from 'next/cache'
interface BookingsProps {
type: string
}
export async function Bookings({ type = 'haircut' }: BookingsProps) {
'use cache'
cacheTag('bookings-data')
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
```
```jsx filename="app/components/bookings.js" switcher
import { cacheTag } from 'next/cache'
export async function Bookings({ type = 'haircut' }) {
'use cache'
cacheTag('bookings-data')
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
```
### Creating tags from external data
You can use the data returned from an async function to tag the cache entry.
```tsx filename="app/components/bookings.tsx" switcher
import { cacheTag } from 'next/cache'
interface BookingsProps {
type: string
}
export async function Bookings({ type = 'haircut' }: BookingsProps) {
async function getBookingsData() {
'use cache'
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
cacheTag('bookings-data', data.id)
return data
}
return //...
}
```
```jsx filename="app/components/bookings.js" switcher
import { cacheTag } from 'next/cache'
export async function Bookings({ type = 'haircut' }) {
async function getBookingsData() {
'use cache'
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
cacheTag('bookings-data', data.id)
return data
}
return //...
}
```
### Invalidating tagged cache
Using [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag), you can invalidate the cache for a specific tag when needed:
```tsx filename="app/actions.ts" switcher
'use server'
import { revalidateTag } from 'next/cache'
export async function updateBookings() {
await updateBookingData()
revalidateTag('bookings-data')
}
```
```jsx filename="app/actions.js" switcher
'use server'
import { revalidateTag } from 'next/cache'
export async function updateBookings() {
await updateBookingData()
revalidateTag('bookings-data')
}
```
@@ -0,0 +1,226 @@
---
title: unstable_catchError
description: API Reference for the unstable_catchError function.
related:
title: Learn more about error handling
links:
- app/getting-started/error-handling
- app/api-reference/file-conventions/error
---
The `unstable_catchError` function creates a component that wraps its children in an error boundary. It provides a programmatic alternative to the [`error.js`](/docs/app/api-reference/file-conventions/error) file convention, enabling component-level error recovery anywhere in your component tree.
Compared to a custom React error boundary, `unstable_catchError` is designed to work with Next.js out of the box:
- **Built-in error recovery** — [`unstable_retry()`](/docs/app/api-reference/file-conventions/error#unstable_retry) re-fetches and re-renders the error boundary's children, including Server Components.
- **Framework-aware integration** — APIs like `redirect()` and `notFound()` work by throwing special errors under the hood. `unstable_catchError` handles these seamlessly, so they're not accidentally caught by your error boundary.
- **Client navigation handling** — The error state automatically clears when you do a client navigation to a different route.
`unstable_catchError` can be called from [Client Components](/docs/app/getting-started/server-and-client-components).
```tsx filename="app/custom-error-boundary.tsx" switcher
'use client'
import { unstable_catchError, type ErrorInfo } from 'next/error'
function ErrorFallback(
props: { title: string },
{ error, unstable_retry }: ErrorInfo
) {
return (
<div>
<h2>{props.title}</h2>
<p>{error.message}</p>
<button onClick={() => unstable_retry()}>Try again</button>
</div>
)
}
export default unstable_catchError(ErrorFallback)
```
```jsx filename="app/custom-error-boundary.js" switcher
'use client'
import { unstable_catchError } from 'next/error'
function ErrorFallback(props, { error, unstable_retry }) {
return (
<div>
<h2>{props.title}</h2>
<p>{error.message}</p>
<button onClick={() => unstable_retry()}>Try again</button>
</div>
)
}
export default unstable_catchError(ErrorFallback)
```
## Reference
### Parameters
`unstable_catchError` accepts a single argument:
```ts
const ErrorWrapper = unstable_catchError(fallback)
```
#### `fallback`
A function that renders the error UI when an error is caught. It receives two arguments:
- `props` — The props passed to the wrapper component (excluding `children`).
- `errorInfo` — An object containing information about the error:
| Property | Type | Description |
| ---------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `error` | [`Error`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error) | The error instance that was caught. |
| `unstable_retry` | `() => void` | Re-fetches and re-renders the error boundary's children. If successful, the fallback is replaced with the re-rendered result. |
| `reset` | `() => void` | Resets the error state and re-renders without re-fetching. Use [`unstable_retry()`](/docs/app/api-reference/file-conventions/error#unstable_retry) in most cases. |
The `fallback` function must be a Client Component (or defined in a `'use client'` module).
### Returns
`unstable_catchError` returns a React component that:
- Accepts the same props as your fallback's first argument, plus `children`.
- Wraps `children` in an error boundary.
- Renders the `fallback` when an error is caught in `children`.
## Examples
### Client Component
Define a fallback and use the returned component to wrap parts of your UI:
```tsx filename="app/some-component.tsx" switcher
import ErrorWrapper from '../custom-error-boundary'
export default function Component({ children }: { children: React.ReactNode }) {
return <ErrorWrapper title="Dashboard Error">{children}</ErrorWrapper>
}
```
```jsx filename="app/some-component.js" switcher
import ErrorWrapper from '../custom-error-boundary'
export default function Component({ children }) {
return <ErrorWrapper title="Dashboard Error">{children}</ErrorWrapper>
}
```
### Recovering from errors
Use `unstable_retry()` to prompt the user to recover from the error. When called, the function re-fetches and re-renders the error boundary's children. If successful, the fallback is replaced with the re-rendered result.
In most cases, use `unstable_retry()` instead of `reset()`. The `reset()` function only clears the error state and re-renders without re-fetching, which means it won't recover from Server Component errors.
```tsx filename="app/custom-error-boundary.tsx" switcher
'use client'
import { unstable_catchError, type ErrorInfo } from 'next/error'
function ErrorFallback(props: {}, { error, unstable_retry, reset }: ErrorInfo) {
return (
<div>
<p>{error.message}</p>
<button onClick={() => unstable_retry()}>Try again</button>
<button onClick={() => reset()}>Reset</button>
</div>
)
}
export default unstable_catchError(ErrorFallback)
```
```jsx filename="app/custom-error-boundary.js" switcher
'use client'
import { unstable_catchError } from 'next/error'
function ErrorFallback(props, { error, unstable_retry, reset }) {
return (
<div>
<p>{error.message}</p>
<button onClick={() => unstable_retry()}>Try again</button>
<button onClick={() => reset()}>Reset</button>
</div>
)
}
export default unstable_catchError(ErrorFallback)
```
### Server-rendered error fallback
You can pass server-rendered content as a prop to display data-driven fallback UI. This works by rendering a Server Component as a `React.ReactNode` prop that the fallback displays when an error is caught.
> **Good to know**: This pattern eagerly renders the fallback on every page render, even when no error occurs. For most use cases, a simpler client-side fallback is sufficient.
```tsx filename="app/error-boundary.tsx" switcher
'use client'
import { unstable_catchError, type ErrorInfo } from 'next/error'
function ErrorFallback(
props: { fallback: React.ReactNode },
errorInfo: ErrorInfo
) {
return props.fallback
}
export default unstable_catchError(ErrorFallback)
```
```jsx filename="app/error-boundary.js" switcher
'use client'
import { unstable_catchError } from 'next/error'
function ErrorFallback(props, errorInfo) {
return props.fallback
}
export default unstable_catchError(ErrorFallback)
```
```tsx filename="app/some-component.tsx" switcher
import ErrorBoundary from '../error-boundary'
async function ErrorFallback() {
const data = await getData()
return <div>{data.message}</div>
}
export default function Component({ children }: { children: React.ReactNode }) {
return <ErrorBoundary fallback={<ErrorFallback />}>{children}</ErrorBoundary>
}
```
```jsx filename="app/some-component.js" switcher
import ErrorBoundary from '../error-boundary'
async function ErrorFallback() {
const data = await getData()
return <div>{data.message}</div>
}
export default function Component({ children }) {
return <ErrorBoundary fallback={<ErrorFallback />}>{children}</ErrorBoundary>
}
```
> **Good to know**:
>
> - Unlike the `error.js` file convention which is scoped to route segments, `unstable_catchError` can be used to wrap any part of your component tree for component-level error recovery.
> - Props passed to the wrapper component are forwarded to the fallback function, making it easy to create reusable error UIs with different configurations.
> - You don't need to wrap `error.js` default exports with `unstable_catchError`. The [`error.js`](/docs/app/api-reference/file-conventions/error) file convention already renders inside a built-in error boundary provided by Next.js.
## Version History
| Version | Changes |
| --------- | --------------------------------- |
| `v16.2.0` | `unstable_catchError` introduced. |
@@ -0,0 +1,58 @@
---
title: connection
description: API Reference for the connection function.
---
The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing.
It's useful when a component doesn't use [Request-time APIs](/docs/app/glossary#request-time-apis), but you want it to be rendered at runtime and not prerendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `new Date()`.
```ts filename="app/page.tsx" switcher
import { connection } from 'next/server'
export default async function Page() {
await connection()
// Everything below will be excluded from prerendering
const rand = Math.random()
return <span>{rand}</span>
}
```
```jsx filename="app/page.js" switcher
import { connection } from 'next/server'
export default async function Page() {
await connection()
// Everything below will be excluded from prerendering
const rand = Math.random()
return <span>{rand}</span>
}
```
## Reference
### Type
```jsx
function connection(): Promise<void>
```
### Parameters
- The function does not accept any parameters.
### Returns
- The function returns a `void` Promise. It is not meant to be consumed.
## Good to know
- `connection` replaces [`unstable_noStore`](/docs/app/api-reference/functions/unstable_noStore) to better align with the future of Next.js.
- The function is only necessary when dynamic rendering is required and common Request-time APIs are not used.
### Version History
| Version | Changes |
| ------------ | ------------------------ |
| `v15.0.0` | `connection` stabilized. |
| `v15.0.0-RC` | `connection` introduced. |
@@ -0,0 +1,301 @@
---
title: cookies
description: API Reference for the cookies function.
---
`cookies` is an **async** function that allows you to read the HTTP incoming request cookies in [Server Components](/docs/app/getting-started/server-and-client-components), and read/write outgoing request cookies in [Server Functions](/docs/app/getting-started/mutating-data) or [Route Handlers](/docs/app/api-reference/file-conventions/route).
```tsx filename="app/page.tsx" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
```js filename="app/page.js" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
## Reference
### Methods
The following methods are available:
| Method | Return Type | Description |
| --------------------------- | ---------------- | ------------------------------------------------------------------------------- |
| `get('name')` | Object | Accepts a cookie name and returns an object with the name and value. |
| `getAll()` | Array of objects | Returns a list of all the cookies with a matching name. |
| `has('name')` | Boolean | Accepts a cookie name and returns a boolean based on if the cookie exists. |
| `set(name, value, options)` | - | Accepts a cookie name, value, and options and sets the outgoing request cookie. |
| `delete(name)` | - | Accepts a cookie name and deletes the cookie. |
| `toString()` | String | Returns a string representation of the cookies. |
### Options
When setting a cookie, the following properties from the `options` object are supported:
| Option | Type | Description |
| ------------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
| `name` | String | Specifies the name of the cookie. |
| `value` | String | Specifies the value to be stored in the cookie. |
| `expires` | Date | Defines the exact date when the cookie will expire. |
| `maxAge` | Number | Sets the cookies lifespan in seconds. |
| `domain` | String | Specifies the domain where the cookie is available. |
| `path` | String, default: `'/'` | Limits the cookie's scope to a specific path within the domain. |
| `secure` | Boolean | Ensures the cookie is sent only over HTTPS connections for added security. |
| `httpOnly` | Boolean | Restricts the cookie to HTTP requests, preventing client-side access. |
| `sameSite` | Boolean, `'lax'`, `'strict'`, `'none'` | Controls the cookie's cross-site request behavior. |
| `priority` | String (`"low"`, `"medium"`, `"high"`) | Specifies the cookie's priority |
| `partitioned` | Boolean | Indicates whether the cookie is [partitioned](https://github.com/privacycg/CHIPS). |
The only option with a default value is `path`.
To learn more about these options, see the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).
## Good to know
- `cookies` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access cookies.
- In version 14 and earlier, `cookies` was a synchronous function. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
- `cookies` is a [Request-time API](/docs/app/glossary#request-time-apis) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/glossary#dynamic-rendering).
- The `.delete` method can only be called:
- In a [Server Function](/docs/app/getting-started/mutating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route).
- If it belongs to the same domain from which `.set` is called. For wildcard domains, the specific subdomain must be an exact match. Additionally, the code must be executed on the same protocol (HTTP or HTTPS) as the cookie you want to delete.
- HTTP does not allow setting cookies after streaming starts, so you must use `.set` in a [Server Function](/docs/app/getting-started/mutating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route).
## Understanding Cookie Behavior in Server Components
When working with cookies in Server Components, it's important to understand that cookies are fundamentally a client-side storage mechanism:
- **Reading cookies** works in Server Components because you're accessing the cookie data that the client's browser sends to the server in the HTTP request headers.
- **Setting cookies** is not supported during Server Component rendering. To modify cookies, invoke a Server Function from the client or use a Route Handler.
The server can only send instructions (via `Set-Cookie` headers) to tell the browser to store cookies - the actual storage happens on the client side. This is why cookie operations that modify state (`.set`, `.delete`) must be performed in a Server Function or Route Handler where the response headers can be properly set.
## Understanding Cookie Behavior in Server Functions
After you set or delete a cookie in a Server Function, Next.js can return both the updated UI and new data in a single server roundtrip when the function is used as a [Server Action](/docs/app/getting-started/mutating-data#what-are-server-functions) (e.g., passed to a form's `action` prop). See [Caching and Revalidating](/docs/app/getting-started/caching).
The UI is not unmounted, but effects that depend on data coming from the server will re-run.
To refresh cached data too, call [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) inside the function.
## Examples
### Getting a cookie
You can use the `(await cookies()).get('name')` method to get a single cookie:
```tsx filename="app/page.tsx" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
```jsx filename="app/page.js" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
```
### Getting all cookies
You can use the `(await cookies()).getAll()` method to get all cookies with a matching name. If `name` is unspecified, it returns all the available cookies.
```tsx filename="app/page.tsx" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
return cookieStore.getAll().map((cookie) => (
<div key={cookie.name}>
<p>Name: {cookie.name}</p>
<p>Value: {cookie.value}</p>
</div>
))
}
```
```jsx filename="app/page.js" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
return cookieStore.getAll().map((cookie) => (
<div key={cookie.name}>
<p>Name: {cookie.name}</p>
<p>Value: {cookie.value}</p>
</div>
))
}
```
### Setting a cookie
You can use the `(await cookies()).set(name, value, options)` method in a [Server Function](/docs/app/getting-started/mutating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route) to set a cookie. The [`options` object](#options) is optional.
```tsx filename="app/actions.ts" switcher
'use server'
import { cookies } from 'next/headers'
export async function create(data) {
const cookieStore = await cookies()
cookieStore.set('name', 'lee')
// or
cookieStore.set('name', 'lee', { secure: true })
// or
cookieStore.set({
name: 'name',
value: 'lee',
httpOnly: true,
path: '/',
})
}
```
```js filename="app/actions.js" switcher
'use server'
import { cookies } from 'next/headers'
export async function create(data) {
const cookieStore = await cookies()
cookieStore.set('name', 'lee')
// or
cookieStore.set('name', 'lee', { secure: true })
// or
cookieStore.set({
name: 'name',
value: 'lee',
httpOnly: true,
path: '/',
})
}
```
### Checking if a cookie exists
You can use the `(await cookies()).has(name)` method to check if a cookie exists:
```tsx filename="app/page.ts" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const hasCookie = cookieStore.has('theme')
return '...'
}
```
```jsx filename="app/page.js" switcher
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const hasCookie = cookieStore.has('theme')
return '...'
}
```
### Deleting cookies
There are three ways you can delete a cookie.
Using the `delete()` method:
```tsx filename="app/actions.ts" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.delete('name')
}
```
```js filename="app/actions.js" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.delete('name')
}
```
Setting a new cookie with the same name and an empty value:
```tsx filename="app/actions.ts" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.set('name', '')
}
```
```js filename="app/actions.js" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.set('name', '')
}
```
Setting the `maxAge` to 0 will immediately expire a cookie. `maxAge` accepts a value in seconds.
```tsx filename="app/actions.ts" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.set('name', 'value', { maxAge: 0 })
}
```
```js filename="app/actions.js" switcher
'use server'
import { cookies } from 'next/headers'
export async function deleteCookie(data) {
const cookieStore = await cookies()
cookieStore.set('name', 'value', { maxAge: 0 })
}
```
## Version History
| Version | Changes |
| ------------ | ------------------------------------------------------------------------------------------------------ |
| `v15.0.0-RC` | `cookies` is now an async function. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
| `v13.0.0` | `cookies` introduced. |
@@ -0,0 +1,137 @@
---
title: draftMode
description: API Reference for the draftMode function.
related:
title: Next Steps
description: Learn how to use Draft Mode with this step-by-step guide.
links:
- app/guides/draft-mode
---
`draftMode` is an **async** function allows you to enable and disable [Draft Mode](/docs/app/guides/draft-mode), as well as check if Draft Mode is enabled in a [Server Component](/docs/app/getting-started/server-and-client-components).
```tsx filename="app/page.ts" switcher
import { draftMode } from 'next/headers'
export default async function Page() {
const { isEnabled } = await draftMode()
}
```
```jsx filename="app/page.js" switcher
import { draftMode } from 'next/headers'
export default async function Page() {
const { isEnabled } = await draftMode()
}
```
## Reference
The following methods and properties are available:
| Method | Description |
| ----------- | --------------------------------------------------------------------------------- |
| `isEnabled` | A boolean value that indicates if Draft Mode is enabled. |
| `enable()` | Enables Draft Mode in a Route Handler by setting a cookie (`__prerender_bypass`). |
| `disable()` | Disables Draft Mode in a Route Handler by deleting a cookie. |
## Good to know
- `draftMode` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function.
- In version 14 and earlier, `draftMode` was a synchronous function. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
- A new bypass cookie value will be generated each time you run `next build`. This ensures that the bypass cookie cant be guessed.
- To test Draft Mode locally over HTTP, your browser will need to allow third-party cookies and local storage access.
## Examples
### Enabling Draft Mode
To enable Draft Mode, create a new [Route Handler](/docs/app/api-reference/file-conventions/route) and call the `enable()` method:
```tsx filename="app/draft/route.ts" switcher
import { draftMode } from 'next/headers'
export async function GET(request: Request) {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}
```
```js filename="app/draft/route.js" switcher
import { draftMode } from 'next/headers'
export async function GET(request) {
const draft = await draftMode()
draft.enable()
return new Response('Draft mode is enabled')
}
```
### Disabling Draft Mode
By default, the Draft Mode session ends when the browser is closed.
To disable Draft Mode manually, call the `disable()` method in your [Route Handler](/docs/app/api-reference/file-conventions/route):
```tsx filename="app/draft/route.ts" switcher
import { draftMode } from 'next/headers'
export async function GET(request: Request) {
const draft = await draftMode()
draft.disable()
return new Response('Draft mode is disabled')
}
```
```js filename="app/draft/route.js" switcher
import { draftMode } from 'next/headers'
export async function GET(request) {
const draft = await draftMode()
draft.disable()
return new Response('Draft mode is disabled')
}
```
Then, send a request to invoke the Route Handler. If calling the route using the [`<Link>` component](/docs/app/api-reference/components/link), you must pass `prefetch={false}` to prevent accidentally deleting the cookie on prefetch.
### Checking if Draft Mode is enabled
You can check if Draft Mode is enabled in a Server Component with the `isEnabled` property:
```tsx filename="app/page.ts" switcher
import { draftMode } from 'next/headers'
export default async function Page() {
const { isEnabled } = await draftMode()
return (
<main>
<h1>My Blog Post</h1>
<p>Draft Mode is currently {isEnabled ? 'Enabled' : 'Disabled'}</p>
</main>
)
}
```
```jsx filename="app/page.js" switcher
import { draftMode } from 'next/headers'
export default async function Page() {
const { isEnabled } = await draftMode()
return (
<main>
<h1>My Blog Post</h1>
<p>Draft Mode is currently {isEnabled ? 'Enabled' : 'Disabled'}</p>
</main>
)
}
```
## Version History
| Version | Changes |
| ------------ | -------------------------------------------------------------------------------------------------------- |
| `v15.0.0-RC` | `draftMode` is now an async function. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
| `v13.4.0` | `draftMode` introduced. |
@@ -0,0 +1,117 @@
---
title: fetch
description: API reference for the extended fetch function.
---
Next.js extends the [Web `fetch()` API](https://developer.mozilla.org/docs/Web/API/Fetch_API) to allow each request on the server to set its own persistent caching and revalidation semantics.
In the browser, the `cache` option indicates how a fetch request will interact with the _browser's_ HTTP cache. With this extension, `cache` indicates how a _server-side_ fetch request will interact with the framework's persistent cache.
You can call `fetch` with `async` and `await` directly within Server Components.
```tsx filename="app/page.tsx" switcher
export default async function Page() {
let data = await fetch('https://api.vercel.app/blog')
let posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
```jsx filename="app/page.js" switcher
export default async function Page() {
let data = await fetch('https://api.vercel.app/blog')
let posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
```
## `fetch(url, options)`
Since Next.js extends the [Web `fetch()` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), you can use any of the [native options available](https://developer.mozilla.org/docs/Web/API/fetch#parameters).
### `options.cache`
Configure how the request should interact with Next.js [caching](/docs/app/getting-started/caching).
```ts
fetch(`https://...`, { cache: 'force-cache' | 'no-store' })
```
- **`auto no cache`** (default): Next.js fetches the resource from the remote server on every request in development, but will fetch once during `next build` because the route will be statically prerendered. If [Request-time APIs](/docs/app/glossary#request-time-apis) are detected on the route, Next.js will fetch the resource on every request.
- **`no-store`**: Next.js fetches the resource from the remote server on every request, even if Request-time APIs are not detected on the route.
- **`force-cache`**: Next.js looks for a matching request in its server-side cache.
- If there is a match and it is fresh, it will be returned from the cache.
- If there is no match or a stale match, Next.js will fetch the resource from the remote server and update the cache with the downloaded resource.
### `options.next.revalidate`
```ts
fetch(`https://...`, { next: { revalidate: false | 0 | number } })
```
Set the cache lifetime of a resource (in seconds).
- **`false`** - Cache the resource indefinitely. Semantically equivalent to `revalidate: Infinity`. The HTTP cache may evict older resources over time.
- **`0`** - Prevent the resource from being cached.
- **`number`** - (in seconds) Specify the resource should have a cache lifetime of at most `n` seconds.
> **Good to know**:
>
> - If an individual `fetch()` request sets a `revalidate` number lower than the [default `revalidate`](/docs/app/guides/caching-without-cache-components#route-segment-config-revalidate) of a route, the whole route revalidation interval will be decreased.
> - If two fetch requests with the same URL in the same route have different `revalidate` values, the lower value will be used.
> - Conflicting options such as `{ revalidate: 3600, cache: 'no-store' }` are not allowed, both will be ignored, and in development mode a warning will be printed to the terminal.
### `options.next.tags`
```ts
fetch(`https://...`, { next: { tags: ['collection'] } })
```
Set the cache tags of a resource. Data can then be revalidated on-demand using [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag). The max length for a custom tag is 256 characters and the max tag items is 128.
## Memoization
`fetch` requests using `GET` with the same URL and options are automatically [memoized](/docs/app/glossary#memoization) during a server render pass. If you call the same `fetch` in multiple Server Components, layouts, pages, `generateStaticParams` and `generateViewport`, Next.js executes it only once and shares the result.
To opt out, pass an [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) signal to `fetch`:
```js
const { signal } = new AbortController()
fetch(url, { signal })
```
> **Good to know**: Memoization does not apply in [Route Handlers](/docs/app/api-reference/file-conventions/route), since they are not part of the React component tree.
## Troubleshooting
### Fetch default `auto no store` and `cache: 'no-store'` not showing fresh data in development
Next.js caches `fetch` responses in Server Components across Hot Module Replacement (HMR) in local development for faster responses and to reduce costs for billed API calls.
By default, the [HMR cache](/docs/app/api-reference/config/next-config-js/serverComponentsHmrCache) applies to all fetch requests, including those with the default `auto no cache` and `cache: 'no-store'` option. This means uncached requests will not show fresh data between HMR refreshes. However, the cache will be cleared on navigation or full-page reloads.
See the [`serverComponentsHmrCache`](/docs/app/api-reference/config/next-config-js/serverComponentsHmrCache) docs for more information.
### Hard refresh and caching in development
In development mode, if the request includes the `cache-control: no-cache` header, `options.cache`, `options.next.revalidate`, and `options.next.tags` are ignored, and the `fetch` request is served from the source.
Browsers typically include `cache-control: no-cache` when the cache is disabled in developer tools or during a hard refresh.
## Version History
| Version | Changes |
| --------- | ------------------- |
| `v13.0.0` | `fetch` introduced. |
@@ -0,0 +1,172 @@
---
title: forbidden
description: API Reference for the forbidden function.
version: experimental
related:
links:
- app/api-reference/file-conventions/forbidden
---
The `forbidden` function throws an error that renders a Next.js 403 error page. It's useful for handling authorization errors in your application. You can customize the UI using the [`forbidden.js` file](/docs/app/api-reference/file-conventions/forbidden).
To start using `forbidden`, enable the experimental [`authInterrupts`](/docs/app/api-reference/config/next-config-js/authInterrupts) configuration option in your `next.config.js` file:
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
authInterrupts: true,
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
module.exports = {
experimental: {
authInterrupts: true,
},
}
```
`forbidden` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Functions](/docs/app/getting-started/mutating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route).
```tsx filename="app/auth/page.tsx" switcher
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const session = await verifySession()
// Check if the user has the 'admin' role
if (session.role !== 'admin') {
forbidden()
}
// Render the admin page for authorized users
return <></>
}
```
```jsx filename="app/auth/page.js" switcher
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const session = await verifySession()
// Check if the user has the 'admin' role
if (session.role !== 'admin') {
forbidden()
}
// Render the admin page for authorized users
return <></>
}
```
## Good to know
- The `forbidden` function cannot be called in the [root layout](/docs/app/api-reference/file-conventions/layout#root-layout).
## Examples
### Role-based route protection
You can use `forbidden` to restrict access to certain routes based on user roles. This ensures that users who are authenticated but lack the required permissions cannot access the route.
```tsx filename="app/admin/page.tsx" switcher
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const session = await verifySession()
// Check if the user has the 'admin' role
if (session.role !== 'admin') {
forbidden()
}
// Render the admin page for authorized users
return (
<main>
<h1>Admin Dashboard</h1>
<p>Welcome, {session.user.name}!</p>
</main>
)
}
```
```jsx filename="app/admin/page.js" switcher
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
export default async function AdminPage() {
const session = await verifySession()
// Check if the user has the 'admin' role
if (session.role !== 'admin') {
forbidden()
}
// Render the admin page for authorized users
return (
<main>
<h1>Admin Dashboard</h1>
<p>Welcome, {session.user.name}!</p>
</main>
)
}
```
### Mutations with Server Actions
When implementing mutations in Server Actions, you can use `forbidden` to only allow users with a specific role to update sensitive data.
```ts filename="app/actions/update-role.ts" switcher
'use server'
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
import db from '@/app/lib/db'
export async function updateRole(formData: FormData) {
const session = await verifySession()
// Ensure only admins can update roles
if (session.role !== 'admin') {
forbidden()
}
// Perform the role update for authorized users
// ...
}
```
```js filename="app/actions/update-role.js" switcher
'use server'
import { verifySession } from '@/app/lib/dal'
import { forbidden } from 'next/navigation'
import db from '@/app/lib/db'
export async function updateRole(formData) {
const session = await verifySession()
// Ensure only admins can update roles
if (session.role !== 'admin') {
forbidden()
}
// Perform the role update for authorized users
// ...
}
```
## Version History
| Version | Changes |
| --------- | ----------------------- |
| `v15.1.0` | `forbidden` introduced. |
@@ -0,0 +1,274 @@
---
title: generateImageMetadata
description: Learn how to generate multiple images in a single Metadata API special file.
related:
title: Next Steps
description: View all the Metadata API options.
links:
- app/api-reference/file-conventions/metadata
---
You can use `generateImageMetadata` to generate different versions of one image or return multiple images for one route segment. This is useful for when you want to avoid hard-coding metadata values, such as for icons.
## Parameters
`generateImageMetadata` function accepts the following parameters:
#### `params` (optional)
An object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) object from the root segment down to the segment `generateImageMetadata` is called from.
```tsx filename="icon.tsx" switcher
export function generateImageMetadata({
params,
}: {
params: { slug: string }
}) {
// ...
}
```
```jsx filename="icon.js" switcher
export function generateImageMetadata({ params }) {
// ...
}
```
| Route | URL | `params` |
| ------------------------------- | ----------- | ------------------------- |
| `app/shop/icon.js` | `/shop` | `undefined` |
| `app/shop/[slug]/icon.js` | `/shop/1` | `{ slug: '1' }` |
| `app/shop/[tag]/[item]/icon.js` | `/shop/1/2` | `{ tag: '1', item: '2' }` |
## Returns
The `generateImageMetadata` function should return an `array` of objects containing the image's metadata such as `alt` and `size`. In addition, each item **must** include an `id` value which will be passed as a promise to the props of the image generating function.
| Image Metadata Object | Type |
| --------------------- | ----------------------------------- |
| `id` | `string` (required) |
| `alt` | `string` |
| `size` | `{ width: number; height: number }` |
| `contentType` | `string` |
```tsx filename="icon.tsx" switcher
import { ImageResponse } from 'next/og'
export function generateImageMetadata() {
return [
{
contentType: 'image/png',
size: { width: 48, height: 48 },
id: 'small',
},
{
contentType: 'image/png',
size: { width: 72, height: 72 },
id: 'medium',
},
]
}
export default async function Icon({ id }: { id: Promise<string | number> }) {
const iconId = await id
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 88,
background: '#000',
color: '#fafafa',
}}
>
Icon {iconId}
</div>
)
)
}
```
```jsx filename="icon.js" switcher
import { ImageResponse } from 'next/og'
export function generateImageMetadata() {
return [
{
contentType: 'image/png',
size: { width: 48, height: 48 },
id: 'small',
},
{
contentType: 'image/png',
size: { width: 72, height: 72 },
id: 'medium',
},
]
}
export default async function Icon({ id }) {
const iconId = await id
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 88,
background: '#000',
color: '#fafafa',
}}
>
Icon {iconId}
</div>
)
)
}
```
## Image generation function props
When using `generateImageMetadata`, the default export image generation function receives the following props:
#### `id`
A promise that resolves to the `id` value from one of the items returned by `generateImageMetadata`. The `id` will be a `string` or `number` depending on what was returned from `generateImageMetadata`.
```tsx filename="icon.tsx" switcher
export default async function Icon({ id }: { id: Promise<string | number> }) {
const iconId = await id
// Use iconId to generate the image
}
```
```jsx filename="icon.js" switcher
export default async function Icon({ id }) {
const iconId = await id
// Use iconId to generate the image
}
```
#### `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 segment the image is colocated in.
```tsx filename="icon.tsx" switcher
export default async function Icon({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// Use slug to generate the image
}
```
```jsx filename="icon.js" switcher
export default async function Icon({ params }) {
const { slug } = await params
// Use slug to generate the image
}
```
### Examples
#### Using external data
This example uses the `params` object and external data to generate multiple [Open Graph images](/docs/app/api-reference/file-conventions/metadata/opengraph-image) for a route segment.
```tsx filename="app/products/[id]/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
import { getCaptionForImage, getOGImages } from '@/app/utils/images'
export async function generateImageMetadata({
params,
}: {
params: { id: string }
}) {
const images = await getOGImages(params.id)
return images.map((image, idx) => ({
id: idx,
size: { width: 1200, height: 600 },
alt: image.text,
contentType: 'image/png',
}))
}
export default async function Image({
params,
id,
}: {
params: Promise<{ id: string }>
id: Promise<number>
}) {
const productId = (await params).id
const imageId = await id
const text = await getCaptionForImage(productId, imageId)
return new ImageResponse(
(
<div
style={
{
// ...
}
}
>
{text}
</div>
)
)
}
```
```jsx filename="app/products/[id]/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
import { getCaptionForImage, getOGImages } from '@/app/utils/images'
export async function generateImageMetadata({ params }) {
const images = await getOGImages(params.id)
return images.map((image, idx) => ({
id: idx,
size: { width: 1200, height: 600 },
alt: image.text,
contentType: 'image/png',
}))
}
export default async function Image({ params, id }) {
const productId = (await params).id
const imageId = await id
const text = await getCaptionForImage(productId, imageId)
return new ImageResponse(
(
<div
style={
{
// ...
}
}
>
{text}
</div>
)
)
}
```
## Version History
| Version | Changes |
| --------- | --------------------------------------------------------------------------------------------------- |
| `v16.0.0` | `id` passed to the Image generation function is now a promise that resolves to `string` or `number` |
| `v16.0.0` | `params` passed to the Image generation function is now a promise that resolves to an object |
| `v13.3.0` | `generateImageMetadata` introduced. |
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
---
title: generateSitemaps
nav_title: generateSitemaps
description: Learn how to use the generateSiteMaps function to create multiple sitemaps for your application.
related:
title: Next Steps
description: Learn how to create sitemaps for your Next.js application.
links:
- app/api-reference/file-conventions/metadata/sitemap
---
You can use the `generateSitemaps` function to generate multiple sitemaps for your application.
## Returns
The `generateSitemaps` returns an array of objects with an `id` property.
## URLs
Your generated sitemaps will be available at `/.../sitemap/[id].xml`. For example, `/product/sitemap/1.xml`.
## Example
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,
}))
}
```
## Version History
| Version | Changes |
| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v16.0.0` | The `id` values returned from `generateSitemaps` are now passed as a promise that resolves to a `string` to the sitemap function. |
| `v15.0.0` | `generateSitemaps` now generates consistent URLs between development and production |
| `v13.3.2` | `generateSitemaps` introduced. In development, you can view the generated sitemap on `/.../sitemap.xml/[id]`. For example, `/product/sitemap.xml/1`. |
@@ -0,0 +1,539 @@
---
title: generateStaticParams
description: API reference for the generateStaticParams function.
---
The `generateStaticParams` function can be used in combination with [dynamic route segments](/docs/app/api-reference/file-conventions/dynamic-routes) to [**statically generate**](/docs/app/glossary#prerendering) routes at build time instead of on-demand at request time.
`generateStaticParams` can be used with:
- [Pages](/docs/app/api-reference/file-conventions/page) (`page.tsx`/`page.js`)
- [Layouts](/docs/app/api-reference/file-conventions/layout) (`layout.tsx`/`layout.js`)
- [Route Handlers](/docs/app/api-reference/file-conventions/route) (`route.ts`/`route.js`)
```tsx filename="app/blog/[slug]/page.tsx" switcher
// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// ...
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default async function Page({ params }) {
const { slug } = await params
// ...
}
```
> **Good to know**:
>
> - You can use the [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config/dynamicParams) segment config option to control what happens when a dynamic segment is visited that was not generated with `generateStaticParams`.
> - You must return [an empty array from `generateStaticParams`](#all-paths-at-build-time) or utilize [`export const dynamic = 'force-static'`](/docs/app/guides/caching-without-cache-components#dynamic) in order to revalidate (ISR) [paths at runtime](#all-paths-at-runtime).
> - During `next dev`, `generateStaticParams` will be called when you navigate to a route.
> - During `next build`, `generateStaticParams` runs before the corresponding Layouts or Pages are generated.
> - During revalidation (ISR), `generateStaticParams` will not be called again.
> - `generateStaticParams` replaces the [`getStaticPaths`](/docs/pages/api-reference/functions/get-static-paths) function in the Pages Router.
## Parameters
`options.params` (optional)
If multiple dynamic segments in a route use `generateStaticParams`, the child `generateStaticParams` function is executed once for each set of `params` the parent generates.
The `params` object contains the populated `params` from the parent `generateStaticParams`, which can be used to [generate the `params` in a child segment](#multiple-dynamic-segments-in-a-route).
## Returns
`generateStaticParams` should return an array of objects where each object represents the populated dynamic segments of a single route.
- Each property in the object is a dynamic segment to be filled in for the route.
- The properties name is the segment's name, and the properties value is what that segment should be filled in with.
| Example Route | `generateStaticParams` Return Type |
| -------------------------------- | ----------------------------------------- |
| `/product/[id]` | `{ id: string }[]` |
| `/products/[category]/[product]` | `{ category: string, product: string }[]` |
| `/products/[...slug]` | `{ slug: string[] }[]` |
## Single Dynamic Segment
```tsx filename="app/product/[id]/page.tsx" switcher
export function generateStaticParams() {
return [{ id: '1' }, { id: '2' }, { id: '3' }]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /product/1
// - /product/2
// - /product/3
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
// ...
}
```
```jsx filename="app/product/[id]/page.js" switcher
export function generateStaticParams() {
return [{ id: '1' }, { id: '2' }, { id: '3' }]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /product/1
// - /product/2
// - /product/3
export default async function Page({ params }) {
const { id } = await params
// ...
}
```
## Multiple Dynamic Segments
```tsx filename="app/products/[category]/[product]/page.tsx" switcher
export function generateStaticParams() {
return [
{ category: 'a', product: '1' },
{ category: 'b', product: '2' },
{ category: 'c', product: '3' },
]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /products/a/1
// - /products/b/2
// - /products/c/3
export default async function Page({
params,
}: {
params: Promise<{ category: string; product: string }>
}) {
const { category, product } = await params
// ...
}
```
```jsx filename="app/products/[category]/[product]/page.js" switcher
export function generateStaticParams() {
return [
{ category: 'a', product: '1' },
{ category: 'b', product: '2' },
{ category: 'c', product: '3' },
]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /products/a/1
// - /products/b/2
// - /products/c/3
export default async function Page({ params }) {
const { category, product } = await params
// ...
}
```
## Catch-all Dynamic Segment
```tsx filename="app/product/[...slug]/page.tsx" switcher
export function generateStaticParams() {
return [{ slug: ['a', '1'] }, { slug: ['b', '2'] }, { slug: ['c', '3'] }]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /product/a/1
// - /product/b/2
// - /product/c/3
export default async function Page({
params,
}: {
params: Promise<{ slug: string[] }>
}) {
const { slug } = await params
// ...
}
```
```jsx filename="app/product/[...slug]/page.js" switcher
export function generateStaticParams() {
return [{ slug: ['a', '1'] }, { slug: ['b', '2'] }, { slug: ['c', '3'] }]
}
// Three versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
// - /product/a/1
// - /product/b/2
// - /product/c/3
export default async function Page({ params }) {
const { slug } = await params
// ...
}
```
## Examples
### Prerendering
#### All paths at build time
To statically render all paths at build time, supply the full list of paths to `generateStaticParams`:
```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,
}))
}
```
#### Subset of paths at build time
To statically render a subset of paths at build time, and the rest the first time they're visited at runtime, return a partial list of paths:
```tsx filename="app/blog/[slug]/page.tsx" switcher
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
// Render the first 10 posts at build time
return posts.slice(0, 10).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())
// Render the first 10 posts at build time
return posts.slice(0, 10).map((post) => ({
slug: post.slug,
}))
}
```
Then, by using the [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config/dynamicParams) segment config option, you can control what happens when a dynamic segment is visited that was not generated with `generateStaticParams`.
```tsx filename="app/blog/[slug]/page.tsx" switcher
// All posts besides the top 10 will be a 404
export const dynamicParams = false
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
const topPosts = posts.slice(0, 10)
return topPosts.map((post) => ({
slug: post.slug,
}))
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
// All posts besides the top 10 will be a 404
export const dynamicParams = false
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
const topPosts = posts.slice(0, 10)
return topPosts.map((post) => ({
slug: post.slug,
}))
}
```
#### All paths at runtime
To statically render all paths the first time they're visited, return an empty array (no paths will be rendered at build time) or utilize [`export const dynamic = 'force-static'`](/docs/app/guides/caching-without-cache-components#dynamic):
```jsx filename="app/blog/[slug]/page.js"
export async function generateStaticParams() {
return []
}
```
> **Good to know:**
>
> - You must always return an array from `generateStaticParams`, even if it's empty. Otherwise, the route will be dynamically rendered.
```jsx filename="app/changelog/[slug]/page.js"
export const dynamic = 'force-static'
```
#### With Cache Components
When using [Cache Components](/docs/app/getting-started/caching) with dynamic routes, `generateStaticParams` must return **at least one param**. Empty arrays cause a [build error](/docs/messages/empty-generate-static-params). This allows Cache Components to validate your route doesn't incorrectly access `cookies()`, `headers()`, or `searchParams` at runtime.
> **Good to know**: If you don't know the actual param values at build time, you can return a placeholder param (e.g., `[{ slug: '__placeholder__' }]`) for validation, then handle it in your page with `notFound()`. However, this prevents build time validation from working effectively and may cause runtime errors.
See the [dynamic routes section](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components) for detailed walkthroughs.
### With Route Handlers
You can use `generateStaticParams` with [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() {
return [{ id: '1' }, { id: '2' }, { id: '3' }]
}
export async function GET(
request: Request,
{ params }: RouteContext<'/api/posts/[id]'>
) {
const { id } = await params
// This will be statically generated for IDs 1, 2, and 3
return Response.json({ id, title: `Post ${id}` })
}
```
```js filename="app/api/posts/[id]/route.js" switcher
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }, { id: '3' }]
}
export async function GET(request, { params }) {
const { id } = await params
// This will be statically generated for IDs 1, 2, and 3
return Response.json({ id, title: `Post ${id}` })
}
```
### Route Handlers with Cache Components
When using [Cache Components](/docs/app/getting-started/caching), combine with `use cache` for optimal caching:
```ts filename="app/api/posts/[id]/route.ts"
export async function generateStaticParams() {
return [{ id: '1' }, { id: '2' }, { id: '3' }]
}
async function getPost(id: Promise<string>) {
'use cache'
const resolvedId = await id
const response = await fetch(`https://api.example.com/posts/${resolvedId}`)
return response.json()
}
export async function GET(
request: Request,
{ params }: RouteContext<'/api/posts/[id]'>
) {
const post = await getPost(params.then((p) => p.id))
return Response.json(post)
}
```
See the [Route Handlers documentation](/docs/app/api-reference/file-conventions/route#static-generation-with-generatestaticparams) for more details.
### Disable rendering for unspecified paths
To prevent unspecified paths from being prerendered at runtime, add the `export const dynamicParams = false` option in a route segment. When this config option is used, only paths provided by `generateStaticParams` will be served, and unspecified routes will 404 or match (in the case of [catch-all routes](/docs/app/api-reference/file-conventions/dynamic-routes#catch-all-segments)).
### Multiple Dynamic Segments in a Route
You can generate params for dynamic segments above the current layout or page, but **not below**. For example, given the `app/products/[category]/[product]` route:
- `app/products/[category]/[product]/page.js` can generate params for **both** `[category]` and `[product]`.
- `app/products/[category]/layout.js` can **only** generate params for `[category]`.
There are two approaches to generating params for a route with multiple dynamic segments:
#### Generate params from the bottom up
Generate multiple dynamic segments from the child route segment.
```tsx filename="app/products/[category]/[product]/page.tsx" switcher
// Generate segments for both [category] and [product]
export async function generateStaticParams() {
const products = await fetch('https://.../products').then((res) => res.json())
return products.map((product) => ({
category: product.category.slug,
product: product.id,
}))
}
export default function Page({
params,
}: {
params: Promise<{ category: string; product: string }>
}) {
// ...
}
```
```jsx filename="app/products/[category]/[product]/page.js" switcher
// Generate segments for both [category] and [product]
export async function generateStaticParams() {
const products = await fetch('https://.../products').then((res) => res.json())
return products.map((product) => ({
category: product.category.slug,
product: product.id,
}))
}
export default function Page({ params }) {
// ...
}
```
#### Generate params from the top down
Generate the parent segments first and use the result to generate the child segments.
```tsx filename="app/products/[category]/layout.tsx" switcher
// Generate segments for [category]
export async function generateStaticParams() {
const products = await fetch('https://.../products').then((res) => res.json())
return products.map((product) => ({
category: product.category.slug,
}))
}
export default function Layout({
params,
}: {
params: Promise<{ category: string }>
}) {
// ...
}
```
```jsx filename="app/products/[category]/layout.js" switcher
// Generate segments for [category]
export async function generateStaticParams() {
const products = await fetch('https://.../products').then((res) => res.json())
return products.map((product) => ({
category: product.category.slug,
}))
}
export default function Layout({ params }) {
// ...
}
```
A child route segment's `generateStaticParams` function is executed once for each segment a parent `generateStaticParams` generates.
The child `generateStaticParams` function can use the `params` returned from the parent `generateStaticParams` function to dynamically generate its own segments.
```tsx filename="app/products/[category]/[product]/page.tsx" switcher
// Generate segments for [product] using the `params` passed from
// the parent segment's `generateStaticParams` function
export async function generateStaticParams({
params: { category },
}: {
params: { category: string }
}) {
const products = await fetch(
`https://.../products?category=${category}`
).then((res) => res.json())
return products.map((product) => ({
product: product.id,
}))
}
export default function Page({
params,
}: {
params: Promise<{ category: string; product: string }>
}) {
// ...
}
```
```jsx filename="app/products/[category]/[product]/page.js" switcher
// Generate segments for [product] using the `params` passed from
// the parent segment's `generateStaticParams` function
export async function generateStaticParams({ params: { category } }) {
const products = await fetch(
`https://.../products?category=${category}`
).then((res) => res.json())
return products.map((product) => ({
product: product.id,
}))
}
export default function Page({ params }) {
// ...
}
```
Notice that the params argument can be accessed synchronously and includes only parent segment params.
For type completion, you can make use of the TypeScript `Awaited` helper in combination with either [`Page Props helper`](/docs/app/api-reference/file-conventions/page#page-props-helper) or [`Layout Props helper`](/docs/app/api-reference/file-conventions/layout#layout-props-helper):
```ts filename="app/products/[category]/[product]/page.tsx" switcher
export async function generateStaticParams({
params: { category },
}: {
params: Awaited<LayoutProps<'/products/[category]'>['params']>
}) {
const products = await fetch(
`https://.../products?category=${category}`
).then((res) => res.json())
return products.map((product) => ({
product: product.id,
}))
}
```
> **Good to know**: `fetch` requests are automatically [memoized](/docs/app/glossary#memoization) for the same data across all `generate`-prefixed functions, Layouts, Pages, and Server Components. React [`cache` can be used](https://react.dev/reference/react/cache) if `fetch` is unavailable.
## Version History
| Version | Changes |
| --------- | ---------------------------------- |
| `v13.0.0` | `generateStaticParams` introduced. |
@@ -0,0 +1,285 @@
---
title: generateViewport
description: API Reference for the generateViewport function.
related:
title: Next Steps
description: View all the Metadata API options.
links:
- app/api-reference/file-conventions/metadata
- app/getting-started/caching
- app/api-reference/config/next-config-js/cacheComponents
---
You can customize the initial viewport of the page with the static `viewport` object or the dynamic `generateViewport` function.
> **Good to know**:
>
> - The `viewport` object and `generateViewport` function exports are **only supported in Server Components**.
> - You cannot export both the `viewport` object and `generateViewport` function from the same route segment.
> - If you're coming from migrating `metadata` exports, you can use [metadata-to-viewport-export codemod](/docs/app/guides/upgrading/codemods#metadata-to-viewport-export) to update your changes.
## The `viewport` object
To define the viewport options, export a `viewport` object from a `layout.jsx` or `page.jsx` file.
```tsx filename="layout.tsx | page.tsx" switcher
import type { Viewport } from 'next'
export const viewport: Viewport = {
themeColor: 'black',
}
export default function Page() {}
```
```jsx filename="layout.jsx | page.jsx" switcher
export const viewport = {
themeColor: 'black',
}
export default function Page() {}
```
## `generateViewport` function
`generateViewport` should return a [`Viewport` object](#viewport-fields) containing one or more viewport fields.
```tsx filename="layout.tsx | page.tsx" switcher
export function generateViewport({ params }) {
return {
themeColor: '...',
}
}
```
In TypeScript, the `params` argument can be typed via [`PageProps<'/route'>`](/docs/app/api-reference/file-conventions/page#page-props-helper) or [`LayoutProps<'/route'>`](/docs/app/api-reference/file-conventions/layout#layout-props-helper) depending on where `generateViewport` is defined.
```jsx filename="layout.js | page.js" switcher
export function generateViewport({ params }) {
return {
themeColor: '...',
}
}
```
> **Good to know**:
>
> - If the viewport doesn't depend on request information, it should be defined using the static [`viewport` object](#the-viewport-object) rather than `generateViewport`.
## Viewport Fields
### `themeColor`
Learn more about [`theme-color`](https://developer.mozilla.org/docs/Web/HTML/Element/meta/name/theme-color).
**Simple theme color**
```tsx filename="layout.tsx | page.tsx" switcher
import type { Viewport } from 'next'
export const viewport: Viewport = {
themeColor: 'black',
}
```
```jsx filename="layout.jsx | page.jsx" switcher
export const viewport = {
themeColor: 'black',
}
```
```html filename="<head> output" hideLineNumbers
<meta name="theme-color" content="black" />
```
**With media attribute**
```tsx filename="layout.tsx | page.tsx" switcher
import type { Viewport } from 'next'
export const viewport: Viewport = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: 'cyan' },
{ media: '(prefers-color-scheme: dark)', color: 'black' },
],
}
```
```jsx filename="layout.jsx | page.jsx" switcher
export const viewport = {
themeColor: [
{ media: '(prefers-color-scheme: light)', color: 'cyan' },
{ media: '(prefers-color-scheme: dark)', color: 'black' },
],
}
```
```html filename="<head> output" hideLineNumbers
<meta name="theme-color" media="(prefers-color-scheme: light)" content="cyan" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />
```
### `width`, `initialScale`, `maximumScale` and `userScalable`
> **Good to know**: The `viewport` meta tag is automatically set, and manual configuration is usually unnecessary as the default is sufficient. However, the information is provided for completeness.
```tsx filename="layout.tsx | page.tsx" switcher
import type { Viewport } from 'next'
export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
// Also supported but less commonly used
// interactiveWidget: 'resizes-visual',
}
```
```jsx filename="layout.jsx | page.jsx" switcher
export const viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
// Also supported but less commonly used
// interactiveWidget: 'resizes-visual',
}
```
```html filename="<head> output" hideLineNumbers
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
```
### `colorScheme`
Learn more about [`color-scheme`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name#:~:text=color%2Dscheme%3A%20specifies,of%20the%20following%3A).
```tsx filename="layout.tsx | page.tsx" switcher
import type { Viewport } from 'next'
export const viewport: Viewport = {
colorScheme: 'dark',
}
```
```jsx filename="layout.jsx | page.jsx" switcher
export const viewport = {
colorScheme: 'dark',
}
```
```html filename="<head> output" hideLineNumbers
<meta name="color-scheme" content="dark" />
```
## With Cache Components
When [Cache Components](/docs/app/getting-started/caching) is enabled, `generateViewport` follows the same rules as other components. If viewport accesses runtime data (`cookies()`, `headers()`, `params`, `searchParams`) or performs uncached data fetching, it defers to request time.
Unlike metadata, viewport cannot be streamed because it affects initial page load UI. If `generateViewport` defers to request time, the page would need to block until resolved.
If viewport depends on external data but not runtime data, use `use cache`:
```tsx filename="app/layout.tsx" highlight={2}
export async function generateViewport() {
'use cache'
const { width, initialScale } = await db.query('viewport-size')
return { width, initialScale }
}
```
If viewport genuinely requires runtime data, wrap the document `<body>` in a Suspense boundary to signal that the entire route should be dynamic:
```tsx filename="app/layout.tsx" highlight={1,13,17}
import { Suspense } from 'react'
import { cookies } from 'next/headers'
export async function generateViewport() {
const cookieJar = await cookies()
return {
themeColor: cookieJar.get('theme-color')?.value,
}
}
export default function RootLayout({ children }) {
return (
<Suspense>
<html>
<body>{children}</body>
</html>
</Suspense>
)
}
```
Caching is preferred because it allows static shell generation. Wrapping the document `body` in Suspense means there is no static shell or content to immediately send when a request arrives, making the entire route block until ready on every request.
> **Good to know**: Use [multiple root layouts](/docs/app/api-reference/file-conventions/layout#root-layout) to isolate fully dynamic viewport to specific routes, while still letting other routes in your application generate a static shell.
## Types
You can add type safety to your viewport object by using the `Viewport` type. If you are using the [built-in TypeScript plugin](/docs/app/api-reference/config/typescript) in your IDE, you do not need to manually add the type, but you can still explicitly add it if you want.
### `viewport` object
```tsx
import type { Viewport } from 'next'
export const viewport: Viewport = {
themeColor: 'black',
}
```
### `generateViewport` function
#### Regular function
```tsx
import type { Viewport } from 'next'
export function generateViewport(): Viewport {
return {
themeColor: 'black',
}
}
```
#### With segment props
```tsx
import type { Viewport } from 'next'
type Props = {
params: Promise<{ id: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export function generateViewport({ params, searchParams }: Props): Viewport {
return {
themeColor: 'black',
}
}
export default function Page({ params, searchParams }: Props) {}
```
#### JavaScript Projects
For JavaScript projects, you can use JSDoc to add type safety.
```js
/** @type {import("next").Viewport} */
export const viewport = {
themeColor: 'black',
}
```
## Version History
| Version | Changes |
| --------- | --------------------------------------------- |
| `v14.0.0` | `viewport` and `generateViewport` introduced. |
@@ -0,0 +1,73 @@
---
title: headers
description: API reference for the headers function.
---
`headers` is an **async** function that allows you to **read** the HTTP incoming request headers from a [Server Component](/docs/app/getting-started/server-and-client-components).
```tsx filename="app/page.tsx" switcher
import { headers } from 'next/headers'
export default async function Page() {
const headersList = await headers()
const userAgent = headersList.get('user-agent')
}
```
```jsx filename="app/page.js" switcher
import { headers } from 'next/headers'
export default async function Page() {
const headersList = await headers()
const userAgent = headersList.get('user-agent')
}
```
## Reference
### Parameters
`headers` does not take any parameters.
### Returns
`headers` returns a **read-only** [Web Headers](https://developer.mozilla.org/docs/Web/API/Headers) object.
- [`Headers.entries()`](https://developer.mozilla.org/docs/Web/API/Headers/entries): Returns an [`iterator`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all key/value pairs contained in this object.
- [`Headers.forEach()`](https://developer.mozilla.org/docs/Web/API/Headers/forEach): Executes a provided function once for each key/value pair in this `Headers` object.
- [`Headers.get()`](https://developer.mozilla.org/docs/Web/API/Headers/get): Returns a [`String`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) sequence of all the values of a header within a `Headers` object with a given name.
- [`Headers.has()`](https://developer.mozilla.org/docs/Web/API/Headers/has): Returns a boolean stating whether a `Headers` object contains a certain header.
- [`Headers.keys()`](https://developer.mozilla.org/docs/Web/API/Headers/keys): Returns an [`iterator`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols) allowing you to go through all keys of the key/value pairs contained in this object.
- [`Headers.values()`](https://developer.mozilla.org/docs/Web/API/Headers/values): Returns an [`iterator`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols) allowing you to go through all values of the key/value pairs contained in this object.
## Good to know
- `headers` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function.
- In version 14 and earlier, `headers` was a synchronous function. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
- Since `headers` is read-only, you cannot `set` or `delete` the outgoing request headers.
- `headers` is a [Request-time API](/docs/app/glossary#request-time-apis) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/glossary#dynamic-rendering)**.
## Examples
### Using the Authorization header
```jsx filename="app/page.js"
import { headers } from 'next/headers'
export default async function Page() {
const authorization = (await headers()).get('authorization')
const res = await fetch('...', {
headers: { authorization }, // Forward the authorization header
})
const user = await res.json()
return <h1>{user.name}</h1>
}
```
## Version History
| Version | Changes |
| ------------ | ------------------------------------------------------------------------------------------------------ |
| `v15.0.0-RC` | `headers` is now an async function. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
| `v13.0.0` | `headers` introduced. |
@@ -0,0 +1,212 @@
---
title: ImageResponse
description: API Reference for the ImageResponse constructor.
---
The `ImageResponse` constructor allows you to generate dynamic images using JSX and CSS. This is useful for generating social media images such as Open Graph images, Twitter cards, and more.
## Reference
### Parameters
The following parameters are available for `ImageResponse`:
```jsx
import { ImageResponse } from 'next/og'
new ImageResponse(
element: ReactElement,
options: {
width?: number = 1200
height?: number = 630
emoji?: 'twemoji' | 'blobmoji' | 'noto' | 'openmoji' = 'twemoji',
fonts?: {
name: string,
data: ArrayBuffer,
weight: number,
style: 'normal' | 'italic'
}[]
debug?: boolean = false
// Options that will be passed to the HTTP response
status?: number = 200
statusText?: string
headers?: Record<string, string>
},
)
```
> Examples are available in the [Vercel OG Playground](https://og-playground.vercel.app/).
### Supported HTML and CSS features
`ImageResponse` supports common CSS properties including flexbox and absolute positioning, custom fonts, text wrapping, centering, and nested images.
Please refer to [Satoris documentation](https://github.com/vercel/satori#css) for a list of supported HTML and CSS features.
## Behavior
- `ImageResponse` uses [@vercel/og](https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation), [Satori](https://github.com/vercel/satori), and Resvg to convert HTML and CSS into PNG.
- Only flexbox and a subset of CSS properties are supported. Advanced layouts (e.g. `display: grid`) will not work.
- Maximum bundle size of `500KB`. The bundle size includes your JSX, CSS, fonts, images, and any other assets. If you exceed the limit, consider reducing the size of any assets or fetching at runtime.
- Only `ttf`, `otf`, and `woff` font formats are supported. To maximize the font parsing speed, `ttf` or `otf` are preferred over `woff`.
## Examples
### Route Handlers
`ImageResponse` can be used in Route Handlers to generate images dynamically at request time.
```js filename="app/api/route.js"
import { ImageResponse } from 'next/og'
export async function GET() {
try {
return new ImageResponse(
(
<div
style={{
height: '100%',
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'white',
padding: '40px',
}}
>
<div
style={{
fontSize: 60,
fontWeight: 'bold',
color: 'black',
textAlign: 'center',
}}
>
Welcome to My Site
</div>
<div
style={{
fontSize: 30,
color: '#666',
marginTop: '20px',
}}
>
Generated with Next.js ImageResponse
</div>
</div>
),
{
width: 1200,
height: 630,
}
)
} catch (e) {
console.log(`${e.message}`)
return new Response(`Failed to generate the image`, {
status: 500,
})
}
}
```
### File-based Metadata
You can use `ImageResponse` in a [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image) file to generate Open Graph images at build time or dynamically at request time.
```tsx filename="app/opengraph-image.tsx"
import { ImageResponse } from 'next/og'
// Image metadata
export const alt = 'My site'
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Image generation
export default async function Image() {
return new ImageResponse(
(
// ImageResponse JSX element
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
My site
</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,
}
)
}
```
### Custom fonts
You can use custom fonts in your `ImageResponse` by providing a `fonts` array in the options.
```tsx filename="app/opengraph-image.tsx"
import { ImageResponse } from 'next/og'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
// Image metadata
export const alt = 'My site'
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 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,
},
],
}
)
}
```
## Version History
| Version | Changes |
| --------- | ----------------------------------------------------- |
| `v14.0.0` | `ImageResponse` moved from `next/server` to `next/og` |
| `v13.3.0` | `ImageResponse` can be imported from `next/server`. |
| `v13.0.0` | `ImageResponse` introduced via `@vercel/og` package. |
@@ -0,0 +1,6 @@
---
title: Functions
description: API Reference for Next.js Functions and Hooks.
---
{/* 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. */}
@@ -0,0 +1,123 @@
---
title: NextRequest
description: API Reference for NextRequest.
---
{/* 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. */}
NextRequest extends the [Web Request API](https://developer.mozilla.org/docs/Web/API/Request) with additional convenience methods.
## `cookies`
Read or mutate the [`Set-Cookie`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie) header of the request.
### `set(name, value)`
Given a name, set a cookie with the given value on the request.
```ts
// Given incoming request /home
// Set a cookie to hide the banner
// request will have a `Set-Cookie:show-banner=false;path=/home` header
request.cookies.set('show-banner', 'false')
```
### `get(name)`
Given a cookie name, return the value of the cookie. If the cookie is not found, `undefined` is returned. If multiple cookies are found, the first one is returned.
```ts
// Given incoming request /home
// { name: 'show-banner', value: 'false', Path: '/home' }
request.cookies.get('show-banner')
```
### `getAll()`
Given a cookie name, return the values of the cookie. If no name is given, return all cookies on the request.
```ts
// Given incoming request /home
// [
// { name: 'experiments', value: 'new-pricing-page', Path: '/home' },
// { name: 'experiments', value: 'winter-launch', Path: '/home' },
// ]
request.cookies.getAll('experiments')
// Alternatively, get all cookies for the request
request.cookies.getAll()
```
### `delete(name)`
Given a cookie name, delete the cookie from the request.
```ts
// Returns true for deleted, false is nothing is deleted
request.cookies.delete('experiments')
```
### `has(name)`
Given a cookie name, return `true` if the cookie exists on the request.
```ts
// Returns true if cookie exists, false if it does not
request.cookies.has('experiments')
```
### `clear()`
Remove all cookies from the request.
```ts
request.cookies.clear()
```
## `nextUrl`
Extends the native [`URL`](https://developer.mozilla.org/docs/Web/API/URL) API with additional convenience methods, including Next.js specific properties.
```ts
// Given a request to /home, pathname is /home
request.nextUrl.pathname
// Given a request to /home?name=lee, searchParams is { 'name': 'lee' }
request.nextUrl.searchParams
```
The following options are available:
<PagesOnly>
| Property | Type | Description |
| ----------------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `basePath` | `string` | The [base path](/docs/pages/api-reference/config/next-config-js/basePath) of the URL. |
| `buildId` | `string` \| `undefined` | The build identifier of the Next.js application. Can be [customized](/docs/pages/api-reference/config/next-config-js/generateBuildId). |
| `defaultLocale` | `string` \| `undefined` | The default locale for [internationalization](/docs/pages/guides/internationalization). |
| `domainLocale` | | |
| - `defaultLocale` | `string` | The default locale within a domain. |
| - `domain` | `string` | The domain associated with a specific locale. |
| - `http` | `boolean` \| `undefined` | Indicates if the domain is using HTTP. |
| `locales` | `string[]` \| `undefined` | An array of available locales. |
| `locale` | `string` \| `undefined` | The currently active locale. |
| `url` | `URL` | The URL object. |
</PagesOnly>
<AppOnly>
| Property | Type | Description |
| -------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `basePath` | `string` | The [base path](/docs/app/api-reference/config/next-config-js/basePath) of the URL. |
| `buildId` | `string` \| `undefined` | The build identifier of the Next.js application. Can be [customized](/docs/app/api-reference/config/next-config-js/generateBuildId). |
| `pathname` | `string` | The pathname of the URL. |
| `searchParams` | `Object` | The search parameters of the URL. |
> **Note:** The internationalization properties from the Pages Router are not available for usage in the App Router. Learn more about [internationalization with the App Router](/docs/app/guides/internationalization).
</AppOnly>
## Version History
| Version | Changes |
| --------- | ----------------------- |
| `v15.0.0` | `ip` and `geo` removed. |
@@ -0,0 +1,203 @@
---
title: NextResponse
description: API Reference for NextResponse.
---
{/* 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. */}
NextResponse extends the [Web Response API](https://developer.mozilla.org/docs/Web/API/Response) with additional convenience methods.
## `cookies`
Read or mutate the [`Set-Cookie`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie) header of the response.
### `set(name, value)`
Given a name, set a cookie with the given value on the response.
```ts
// Given incoming request /home
let response = NextResponse.next()
// Set a cookie to hide the banner
response.cookies.set('show-banner', 'false')
// Response will have a `Set-Cookie:show-banner=false;path=/home` header
return response
```
### `get(name)`
Given a cookie name, return the value of the cookie. If the cookie is not found, `undefined` is returned. If multiple cookies are found, the first one is returned.
```ts
// Given incoming request /home
let response = NextResponse.next()
// { name: 'show-banner', value: 'false', Path: '/home' }
response.cookies.get('show-banner')
```
### `getAll()`
Given a cookie name, return the values of the cookie. If no name is given, return all cookies on the response.
```ts
// Given incoming request /home
let response = NextResponse.next()
// [
// { name: 'experiments', value: 'new-pricing-page', Path: '/home' },
// { name: 'experiments', value: 'winter-launch', Path: '/home' },
// ]
response.cookies.getAll('experiments')
// Alternatively, get all cookies for the response
response.cookies.getAll()
```
### `has(name)`
Given a cookie name, return `true` if the cookie exists on the response.
```ts
// Given incoming request /home
let response = NextResponse.next()
// Returns true if cookie exists, false if it does not
response.cookies.has('experiments')
```
### `delete(name)`
Given a cookie name, delete the cookie from the response.
```ts
// Given incoming request /home
let response = NextResponse.next()
// Returns true for deleted, false if nothing is deleted
response.cookies.delete('experiments')
```
## `json()`
Produce a response with the given JSON body.
```ts filename="app/api/route.ts" switcher
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
```
```js filename="app/api/route.js" switcher
import { NextResponse } from 'next/server'
export async function GET(request) {
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
```
## `redirect()`
Produce a response that redirects to a [URL](https://developer.mozilla.org/docs/Web/API/URL).
```ts
import { NextResponse } from 'next/server'
return NextResponse.redirect(new URL('/new', request.url))
```
The [URL](https://developer.mozilla.org/docs/Web/API/URL) can be created and modified before being used in the `NextResponse.redirect()` method. For example, you can use the `request.nextUrl` property to get the current URL, and then modify it to redirect to a different URL.
```ts
import { NextResponse } from 'next/server'
// Given an incoming request...
const loginUrl = new URL('/login', request.url)
// Add ?from=/incoming-url to the /login URL
loginUrl.searchParams.set('from', request.nextUrl.pathname)
// And redirect to the new URL
return NextResponse.redirect(loginUrl)
```
## `rewrite()`
Produce a response that rewrites (proxies) the given [URL](https://developer.mozilla.org/docs/Web/API/URL) while preserving the original URL.
```ts
import { NextResponse } from 'next/server'
// Incoming request: /about, browser shows /about
// Rewritten request: /proxy, browser shows /about
return NextResponse.rewrite(new URL('/proxy', request.url))
```
## `next()`
The `next()` method is useful for Proxy, as it allows you to return early and continue routing.
```ts
import { NextResponse } from 'next/server'
return NextResponse.next()
```
You can also forward `headers` upstream when producing the response, using `NextResponse.next({ request: { headers } })`:
```ts
import { NextResponse } from 'next/server'
// Given an incoming request...
const newHeaders = new Headers(request.headers)
// Add a new header
newHeaders.set('x-version', '123')
// Forward the modified request headers upstream
return NextResponse.next({
request: {
// New request headers
headers: newHeaders,
},
})
```
This forwards `newHeaders` upstream to the target page, route, or server action, and does not expose them to the client. While this pattern is useful for passing data upstream, it should be used with caution because the headers containing this data may be forwarded to external services.
In contrast, `NextResponse.next({ headers })` is a shorthand for sending headers from proxy to the client. This is **NOT** good practice and should be avoided. Among other reasons because setting response headers like `Content-Type`, can override framework expectations (for example, the `Content-Type` used by Server Actions), leading to failed submissions or broken streaming responses.
```ts
import { type NextRequest, NextResponse } from 'next/server'
async function proxy(request: NextRequest) {
const headers = await injectAuth(request.headers)
// DO NOT forward headers like this
return NextResponse.next({ headers })
}
```
In general, avoid copying all incoming request headers because doing so can leak sensitive data to clients or upstream services.
Prefer a defensive approach by creating a subset of incoming request headers using an allow-list. For example, you might discard custom `x-*` headers and only forward known-safe headers:
```ts
import { type NextRequest, NextResponse } from 'next/server'
function proxy(request: NextRequest) {
const incoming = new Headers(request.headers)
const forwarded = new Headers()
for (const [name, value] of incoming) {
const headerName = name.toLowerCase()
// Keep only known-safe headers, discard custom x-* and other sensitive ones
if (
!headerName.startsWith('x-') &&
headerName !== 'authorization' &&
headerName !== 'cookie'
) {
// Preserve original header name casing
forwarded.set(name, value)
}
}
return NextResponse.next({
request: {
headers: forwarded,
},
})
}
```
@@ -0,0 +1,39 @@
---
title: notFound
description: API Reference for the notFound function.
---
The `notFound` function allows you to render the [`not-found file`](/docs/app/api-reference/file-conventions/not-found) within a route segment as well as inject a [`<meta name="robots" content="noindex" />`](/docs/app/api-reference/file-conventions/loading#status-codes) tag for search engines.
## `notFound()`
Invoking the `notFound()` function throws a `NEXT_HTTP_ERROR_FALLBACK;404` error and terminates rendering of the route segment in which it was thrown. Specifying a [**not-found** file](/docs/app/api-reference/file-conventions/not-found) allows you to gracefully handle such errors by rendering a Not Found UI within the segment.
```jsx filename="app/user/[id]/page.js"
import { notFound } from 'next/navigation'
async function fetchUser(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const { id } = await params
const user = await fetchUser(id)
if (!user) {
notFound()
}
// ...
}
```
> **Good to know**: `notFound()` does not require you to use `return notFound()` due to using the TypeScript [`never`](https://www.typescriptlang.org/docs/handbook/2/functions.html#never) type.
## Version History
| Version | Changes |
| --------- | ---------------------- |
| `v13.0.0` | `notFound` introduced. |
@@ -0,0 +1,72 @@
---
title: permanentRedirect
description: API Reference for the permanentRedirect function.
related:
links:
- app/api-reference/functions/redirect
---
The `permanentRedirect` function allows you to redirect the user to another URL. `permanentRedirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/mutating-data).
When used in a streaming context, this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 308 (Permanent) HTTP redirect response to the caller.
If a resource doesn't exist, you can use the [`notFound` function](/docs/app/api-reference/functions/not-found) instead.
> **Good to know**: If you prefer to return a 307 (Temporary) HTTP redirect instead of 308 (Permanent), you can use the [`redirect` function](/docs/app/api-reference/functions/redirect) instead.
## Parameters
The `permanentRedirect` function accepts two arguments:
```js
permanentRedirect(path, type)
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------- | ----------------------------------------------------------- |
| `path` | `string` | The URL to redirect to. Can be a relative or absolute path. |
| `type` | `'replace'` (default) or `'push'` (default in Server Actions) | The type of redirect to perform. |
By default, `permanentRedirect` will use `push` (adding a new entry to the browser history stack) in [Server Actions](/docs/app/getting-started/mutating-data) and `replace` (replacing the current URL in the browser history stack) everywhere else. You can override this behavior by specifying the `type` parameter.
The `RedirectType` object contains the available options for the `type` parameter.
```ts
import { permanentRedirect, RedirectType } from 'next/navigation'
permanentRedirect('/redirect-to', RedirectType.replace)
// or
permanentRedirect('/redirect-to', RedirectType.push)
```
The `type` parameter has no effect when used in Server Components.
## Returns
`permanentRedirect` does not return a value.
## Example
Invoking the `permanentRedirect()` function throws a `NEXT_REDIRECT` error and terminates rendering of the route segment in which it was thrown.
```jsx filename="app/team/[id]/page.js"
import { permanentRedirect } from 'next/navigation'
async function fetchTeam(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const { id } = await params
const team = await fetchTeam(id)
if (!team) {
permanentRedirect('/login')
}
// ...
}
```
> **Good to know**: `permanentRedirect` does not require you to use `return permanentRedirect()` as it uses the TypeScript [`never`](https://www.typescriptlang.org/docs/handbook/2/functions.html#never) type.
@@ -0,0 +1,222 @@
---
title: redirect
description: API Reference for the redirect function.
related:
links:
- app/api-reference/functions/permanentRedirect
---
The `redirect` function allows you to redirect the user to another URL. `redirect` can be used while rendering in [Server and Client Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/mutating-data).
When used in a [streaming context](/docs/app/getting-started/linking-and-navigating#streaming), this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 307 HTTP redirect response to the caller.
If a resource doesn't exist, you can use the [`notFound` function](/docs/app/api-reference/functions/not-found) instead.
## Reference
### Parameters
The `redirect` function accepts two arguments:
```js
redirect(path, type)
```
| Parameter | Type | Description |
| --------- | ------------------------------------------------------------- | ----------------------------------------------------------- |
| `path` | `string` | The URL to redirect to. Can be a relative or absolute path. |
| `type` | `'replace'` (default) or `'push'` (default in Server Actions) | The type of redirect to perform. |
By default, `redirect` will use `push` (adding a new entry to the browser history stack) in [Server Actions](/docs/app/getting-started/mutating-data) and `replace` (replacing the current URL in the browser history stack) everywhere else. You can override this behavior by specifying the `type` parameter.
The `RedirectType` object contains the available options for the `type` parameter.
```ts
import { redirect, RedirectType } from 'next/navigation'
redirect('/redirect-to', RedirectType.replace)
// or
redirect('/redirect-to', RedirectType.push)
```
The `type` parameter has no effect when used in Server Components.
### Returns
`redirect` does not return a value.
## Behavior
- In Server Actions and Route Handlers, redirect should be called **outside** the `try` block when using `try/catch` statements.
- If you prefer to return a 308 (Permanent) HTTP redirect instead of 307 (Temporary), you can use the [`permanentRedirect` function](/docs/app/api-reference/functions/permanentRedirect) instead.
- `redirect` throws an error so it should be called **outside** the `try` block when using `try/catch` statements.
- `redirect` can be called in Client Components during the rendering process but not in event handlers. You can use the [`useRouter` hook](/docs/app/api-reference/functions/use-router) instead.
- `redirect` also accepts absolute URLs and can be used to redirect to external links.
- If you'd like to redirect before the render process, use [`next.config.js`](/docs/app/guides/redirecting#redirects-in-nextconfigjs) or [Proxy](/docs/app/guides/redirecting#nextresponseredirect-in-proxy).
## Example
### Server Component
Invoking the `redirect()` function throws a `NEXT_REDIRECT` error and terminates rendering of the route segment in which it was thrown.
```tsx filename="app/team/[id]/page.tsx" switcher
import { redirect } from 'next/navigation'
async function fetchTeam(id: string) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const team = await fetchTeam(id)
if (!team) {
redirect('/login')
}
// ...
}
```
```jsx filename="app/team/[id]/page.js" switcher
import { redirect } from 'next/navigation'
async function fetchTeam(id) {
const res = await fetch('https://...')
if (!res.ok) return undefined
return res.json()
}
export default async function Profile({ params }) {
const { id } = await params
const team = await fetchTeam(id)
if (!team) {
redirect('/login')
}
// ...
}
```
> **Good to know**: `redirect` does not require you to use `return redirect()` as it uses the TypeScript [`never`](https://www.typescriptlang.org/docs/handbook/2/functions.html#never) type.
### Client Component
`redirect` can be directly used in a Client Component.
```tsx filename="components/client-redirect.tsx" switcher
'use client'
import { redirect, usePathname } from 'next/navigation'
export function ClientRedirect() {
const pathname = usePathname()
if (pathname.startsWith('/admin') && !pathname.includes('/login')) {
redirect('/admin/login')
}
return <div>Login Page</div>
}
```
```jsx filename="components/client-redirect.jsx" switcher
'use client'
import { redirect, usePathname } from 'next/navigation'
export function ClientRedirect() {
const pathname = usePathname()
if (pathname.startsWith('/admin') && !pathname.includes('/login')) {
redirect('/admin/login')
}
return <div>Login Page</div>
}
```
> **Good to know**: When using `redirect` in a Client Component on initial page load during Server-Side Rendering (SSR), it will perform a server-side redirect.
`redirect` can be used in a Client Component through a Server Action. If you need to use an event handler to redirect the user, you can use the [`useRouter`](/docs/app/api-reference/functions/use-router) hook.
```tsx filename="app/client-redirect.tsx" switcher
'use client'
import { navigate } from './actions'
export function ClientRedirect() {
return (
<form action={navigate}>
<input type="text" name="id" />
<button>Submit</button>
</form>
)
}
```
```jsx filename="app/client-redirect.jsx" switcher
'use client'
import { navigate } from './actions'
export function ClientRedirect() {
return (
<form action={navigate}>
<input type="text" name="id" />
<button>Submit</button>
</form>
)
}
```
```ts filename="app/actions.ts" switcher
'use server'
import { redirect } from 'next/navigation'
export async function navigate(data: FormData) {
redirect(`/posts/${data.get('id')}`)
}
```
```js filename="app/actions.js" switcher
'use server'
import { redirect } from 'next/navigation'
export async function navigate(data) {
redirect(`/posts/${data.get('id')}`)
}
```
## FAQ
### Why does `redirect` use 307 and 308?
When using `redirect()` you may notice that the status codes used are `307` for a temporary redirect, and `308` for a permanent redirect. While traditionally a `302` was used for a temporary redirect, and a `301` for a permanent redirect, many browsers changed the request method of the redirect, from a `POST` to `GET` request when using a `302`, regardless of the origins request method.
Taking the following example of a redirect from `/users` to `/people`, if you make a `POST` request to `/users` to create a new user, and are conforming to a `302` temporary redirect, the request method will be changed from a `POST` to a `GET` request. This doesn't make sense, as to create a new user, you should be making a `POST` request to `/people`, and not a `GET` request.
The introduction of the `307` status code means that the request method is preserved as `POST`.
- `302` - Temporary redirect, will change the request method from `POST` to `GET`
- `307` - Temporary redirect, will preserve the request method as `POST`
The `redirect()` method uses a `307` by default, instead of a `302` temporary redirect, meaning your requests will _always_ be preserved as `POST` requests.
[Learn more](https://developer.mozilla.org/docs/Web/HTTP/Redirections) about HTTP Redirects.
## Version History
| Version | Changes |
| --------- | ---------------------- |
| `v13.0.0` | `redirect` introduced. |
@@ -0,0 +1,69 @@
---
title: refresh
description: API Reference for the refresh function.
---
`refresh` allows you to refresh the client router from within a [Server Action](/docs/app/getting-started/mutating-data).
## Usage
`refresh` can **only** be called from within Server Actions. It cannot be used in Route Handlers, Client Components, or any other context.
## Parameters
```tsx
refresh(): void;
```
## Returns
`refresh` does not return a value.
## Examples
```ts filename="app/actions.ts" switcher
'use server'
import { refresh } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
// Create the post in your database
const post = await db.post.create({
data: { title, content },
})
refresh()
}
```
```js filename="app/actions.js" switcher
'use server'
import { refresh } from 'next/cache'
export async function createPost(formData) {
const title = formData.get('title')
const content = formData.get('content')
// Create the post in your database
const post = await db.post.create({
data: { title, content },
})
refresh()
}
```
### Error when used outside Server Actions
```ts filename="app/api/posts/route.ts" switcher
import { refresh } from 'next/cache'
export async function POST() {
// This will throw an error
refresh()
}
```
@@ -0,0 +1,226 @@
---
title: revalidatePath
description: API Reference for the revalidatePath function.
---
`revalidatePath` allows you to invalidate [cached data](/docs/app/getting-started/caching) on-demand for a specific path.
## Usage
`revalidatePath` can be called in Server Functions and Route Handlers.
`revalidatePath` cannot be called in Client Components or Proxy, as it only works in server environments.
> **Good to know**:
>
> - **Server Functions**: Updates the UI immediately (if viewing the affected path). Currently, it also causes all previously visited pages to refresh when navigated to again. This behavior is temporary and will be updated in the future to apply only to the specific path.
> - **Route Handlers**: Marks the path for revalidation. The revalidation is done on the next visit to the specified path. This means calling `revalidatePath` with a dynamic route segment will not immediately trigger many revalidations at once. The invalidation only happens when the path is next visited.
## Parameters
```tsx
revalidatePath(path: string, type?: 'page' | 'layout'): void;
```
- `path`: Either a string that represents your route file structure. This can be a literal path like `/product/123`, or a route pattern with dynamic segments like `/product/[slug]`. Do not append `/page` or `/layout`, use the `type` parameter instead. Must not exceed 1024 characters. This value is case-sensitive. You do not need to include a trailing slash, regardless of your [`trailingSlash`](/docs/app/api-reference/config/next-config-js/trailingSlash) config.
- `type`: (optional) `'page'` or `'layout'` string to change the type of path to revalidate. If `path` contains a dynamic segment, for example `/product/[slug]`, this parameter is required. If `path` is a literal path like `/product/1`, omit `type`.
Use a literal path when you want to refresh a [single page](#revalidating-a-specific-path). Use a route pattern plus `type` to refresh [all matching pages](#revalidating-a-page-path).
## Returns
`revalidatePath` does not return a value.
## What can be invalidated
The path parameter can point to pages, layouts, or route handlers:
- **Pages**: Invalidates the specific page
- **Layouts**: Invalidates the layout (the `layout.tsx` at that segment), all nested layouts beneath it, and all pages beneath them
- **Route Handlers**: Invalidates cached data accessed within route handlers. For example `revalidatePath("/api/data")` invalidates this GET handler:
```ts filename="app/api/data/route.ts"
export async function GET() {
const data = await fetch('https://api.vercel.app/blog', {
cache: 'force-cache',
})
return Response.json(await data.json())
}
```
## Using `revalidatePath` with rewrites
When using [rewrites](/docs/app/api-reference/config/next-config-js/rewrites), you must pass the **destination** path (the actual route file location), not the source path that appears in the browser's address bar.
For example, if you have a rewrite from `/blog` to `/news`:
```js filename="next.config.js"
module.exports = {
async rewrites() {
return [
{
source: '/blog',
destination: '/news',
},
]
},
}
```
To revalidate this page, use the destination path:
```ts
// Correct: use the destination path
revalidatePath('/news')
// Incorrect: the source path won't match the cache entry
revalidatePath('/blog')
```
This is because `revalidatePath` operates on the route file structure, not the URL visible to users. Cache entries are tagged based on which route file renders them.
## Relationship with `revalidateTag` and `updateTag`
`revalidatePath`, [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) and [`updateTag`](/docs/app/api-reference/functions/updateTag) serve different purposes:
- **`revalidatePath`**: Invalidates a specific page or layout path
- **`revalidateTag`**: Marks data with specific tags as **stale**. Applies across all pages that use those tags
- **`updateTag`**: Expires data with specific tags. Applies across all pages that use those tags
When you call `revalidatePath`, only the specified path gets fresh data on the next visit. Other pages that use the same data tags will continue to serve cached data until those specific tags are also revalidated:
```tsx
// Page A: /blog
const posts = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
// Page B: /dashboard
const recentPosts = await fetch('https://api.vercel.app/blog?limit=5', {
next: { tags: ['posts'] },
})
```
After calling `revalidatePath('/blog')`:
- **Page A (/blog)**: Shows fresh data (page re-rendered)
- **Page B (/dashboard)**: Still shows stale data (cache tag 'posts' not invalidated)
Learn about the difference between [`revalidateTag` and `updateTag`](/docs/app/api-reference/functions/updateTag#differences-from-revalidatetag).
### Building revalidation utilities
`revalidatePath` and `updateTag` are complementary primitives that are often used together in utility functions to ensure comprehensive data consistency across your application:
```ts
'use server'
import { revalidatePath, updateTag } from 'next/cache'
export async function updatePost() {
await updatePostInDatabase()
revalidatePath('/blog') // Refresh the blog page
updateTag('posts') // Refresh all pages using 'posts' tag
}
```
This pattern ensures that both the specific page and any other pages using the same data remain consistent.
## Examples
### Revalidating a specific path
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/post-1')
```
This will invalidate one specific path for revalidation on the next page visit.
### Revalidating a Page path
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'page')
// or with route groups
revalidatePath('/(main)/blog/[slug]', 'page')
```
This will invalidate any path that matches the provided `page` file for revalidation on the next page visit. This will _not_ invalidate pages beneath the specific page. For example, `/blog/[slug]` won't invalidate `/blog/[slug]/[author]`.
### Revalidating a Layout path
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/blog/[slug]', 'layout')
// or with route groups
revalidatePath('/(main)/post/[slug]', 'layout')
```
This will invalidate any path that matches the provided `layout` file for revalidation on the next page visit. This will cause pages beneath with the same layout to be invalidated and revalidated on the next visit. For example, in the above case, `/blog/[slug]/[another]` would also be invalidated and revalidated on the next visit.
### Revalidating all data
```ts
import { revalidatePath } from 'next/cache'
revalidatePath('/', 'layout')
```
This will purge the [Client Cache](/docs/app/glossary#client-cache), and invalidate all cached data for revalidation on the next page visit.
### Server Function
```ts filename="app/actions.ts" switcher
'use server'
import { revalidatePath } from 'next/cache'
export default async function submit() {
await submitForm()
revalidatePath('/')
}
```
### Route Handler
```ts filename="app/api/revalidate/route.ts" switcher
import { revalidatePath } from 'next/cache'
import type { NextRequest } from 'next/server'
export async function GET(request: NextRequest) {
const path = request.nextUrl.searchParams.get('path')
if (path) {
revalidatePath(path)
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({
revalidated: false,
now: Date.now(),
message: 'Missing path to revalidate',
})
}
```
```js filename="app/api/revalidate/route.js" switcher
import { revalidatePath } from 'next/cache'
export async function GET(request) {
const path = request.nextUrl.searchParams.get('path')
if (path) {
revalidatePath(path)
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({
revalidated: false,
now: Date.now(),
message: 'Missing path to revalidate',
})
}
```
@@ -0,0 +1,136 @@
---
title: revalidateTag
description: API Reference for the revalidateTag function.
---
`revalidateTag` allows you to invalidate cached data on-demand for a specific cache tag.
This function is ideal for content where a slight delay in updates is acceptable, such as blog posts, product catalogs, or documentation. Users receive stale content while fresh data loads in the background.
## Usage
`revalidateTag` can be called in Server Functions and Route Handlers.
`revalidateTag` cannot be called in Client Components or Proxy, as it only works in server environments.
### Revalidation Behavior
The revalidation behavior depends on whether you provide the second argument:
- **With `profile="max"` (recommended)**: The tag entry is marked as stale, and the next time a resource with that tag is visited, it will use stale-while-revalidate semantics. This means the stale content is served while fresh content is fetched in the background.
- **With a custom cache life profile**: For advanced usage, you can specify any cache life profile that your application has defined, allowing for custom revalidation behaviors tailored to your specific caching requirements.
- **Without the second argument (deprecated)**: The tag entry is expired immediately, and the next request to that resource will be a blocking revalidate/cache miss. This behavior is now deprecated, and you should either use `profile="max"` or migrate to [`updateTag`](/docs/app/api-reference/functions/updateTag).
> **Good to know**: When using `profile="max"`, `revalidateTag` marks tagged data as stale, but fresh data is only fetched when pages using that tag are next visited. This means calling `revalidateTag` will not immediately trigger many revalidations at once. The invalidation only happens when any page using that tag is next visited.
## Parameters
```ts
revalidateTag(tag: string, profile: string | { expire?: number }): void;
```
- `tag`: A string representing the cache tag associated with the data you want to revalidate. Must not exceed 256 characters. This value is case-sensitive.
- `profile`: A string that specifies the revalidation behavior. The recommended value is `"max"` which provides stale-while-revalidate semantics, or any of the other default or custom profiles defined in [`cacheLife`](/docs/app/api-reference/config/next-config-js/cacheLife). Alternatively, you can pass an object with an `expire` property for custom expiration behavior.
Tags must first be assigned to cached data. You can do this in two ways:
- Using the [`next.tags`](/docs/app/api-reference/functions/fetch) option with `fetch` for caching external API requests:
```tsx
fetch(url, { next: { tags: ['posts'] } })
```
- Using [`cacheTag`](/docs/app/api-reference/functions/cacheTag) inside cached functions or components with the `'use cache'` directive:
```tsx
import { cacheTag } from 'next/cache'
async function getData() {
'use cache'
cacheTag('posts')
// ...
}
```
> **Good to know**: The single-argument form `revalidateTag(tag)` is deprecated. It currently works if TypeScript errors are suppressed, but this behavior may be removed in a future version. Update to the two-argument signature.
## Returns
`revalidateTag` does not return a value.
## Relationship with `revalidatePath`
`revalidateTag` invalidates data with specific tags across all pages that use those tags, while [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) invalidates specific page or layout paths.
> **Good to know**: These functions serve different purposes and may need to be used together for comprehensive data consistency. For detailed examples and considerations, see [relationship with revalidateTag and updateTag](/docs/app/api-reference/functions/revalidatePath#relationship-with-revalidatetag-and-updatetag) for more information.
## Examples
The following examples demonstrate how to use `revalidateTag` in different contexts. In both cases, we're using `profile="max"` to mark data as stale and use stale-while-revalidate semantics, which is the recommended approach for most use cases.
### Server Action
```ts filename="app/actions.ts" switcher
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('posts', 'max')
}
```
```js filename="app/actions.js" switcher
'use server'
import { revalidateTag } from 'next/cache'
export default async function submit() {
await addPost()
revalidateTag('posts', 'max')
}
```
### Route Handler
```ts filename="app/api/revalidate/route.ts" switcher
import type { NextRequest } from 'next/server'
import { revalidateTag } from 'next/cache'
export async function GET(request: NextRequest) {
const tag = request.nextUrl.searchParams.get('tag')
if (tag) {
revalidateTag(tag, 'max')
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({
revalidated: false,
now: Date.now(),
message: 'Missing tag to revalidate',
})
}
```
```js filename="app/api/revalidate/route.js" switcher
import { revalidateTag } from 'next/cache'
export async function GET(request) {
const tag = request.nextUrl.searchParams.get('tag')
if (tag) {
revalidateTag(tag, 'max')
return Response.json({ revalidated: true, now: Date.now() })
}
return Response.json({
revalidated: false,
now: Date.now(),
message: 'Missing tag to revalidate',
})
}
```
> **Good to know**: For webhooks or third-party services that need immediate expiration, you can pass `{ expire: 0 }` as the second argument: `revalidateTag(tag, { expire: 0 })`. This pattern is necessary when external systems call your Route Handlers and require data to expire immediately. For all other cases, it's recommended to use [`updateTag`](/docs/app/api-reference/functions/updateTag) in Server Actions for immediate updates instead.
@@ -0,0 +1,234 @@
---
title: unauthorized
description: API Reference for the unauthorized function.
version: experimental
related:
links:
- app/api-reference/file-conventions/unauthorized
---
The `unauthorized` function throws an error that renders a Next.js 401 error page. It's useful for handling authorization errors in your application. You can customize the UI using the [`unauthorized.js` file](/docs/app/api-reference/file-conventions/unauthorized).
To start using `unauthorized`, enable the experimental [`authInterrupts`](/docs/app/api-reference/config/next-config-js/authInterrupts) configuration option in your `next.config.js` file:
```ts filename="next.config.ts" switcher
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
authInterrupts: true,
},
}
export default nextConfig
```
```js filename="next.config.js" switcher
module.exports = {
experimental: {
authInterrupts: true,
},
}
```
`unauthorized` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Functions](/docs/app/getting-started/mutating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route).
```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()
}
// Render the dashboard for authenticated users
return (
<main>
<h1>Welcome to the Dashboard</h1>
<p>Hi, {session.user.name}.</p>
</main>
)
}
```
```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()
}
// Render the dashboard for authenticated users
return (
<main>
<h1>Welcome to the Dashboard</h1>
<p>Hi, {session.user.name}.</p>
</main>
)
}
```
## Good to know
- The `unauthorized` function cannot be called in the [root layout](/docs/app/api-reference/file-conventions/layout#root-layout).
## Examples
### Displaying login UI to unauthenticated users
You can use `unauthorized` function to display 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>
)
}
```
### Mutations with Server Actions
You can invoke `unauthorized` in Server Actions to ensure only authenticated users can perform specific mutations.
```ts filename="app/actions/update-profile.ts" switcher
'use server'
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
import db from '@/app/lib/db'
export async function updateProfile(data: FormData) {
const session = await verifySession()
// If the user is not authenticated, return a 401
if (!session) {
unauthorized()
}
// Proceed with mutation
// ...
}
```
```js filename="app/actions/update-profile.js" switcher
'use server'
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
import db from '@/app/lib/db'
export async function updateProfile(data) {
const session = await verifySession()
// If the user is not authenticated, return a 401
if (!session) {
unauthorized()
}
// Proceed with mutation
// ...
}
```
### Fetching data with Route Handlers
You can use `unauthorized` in Route Handlers to ensure only authenticated users can access the endpoint.
```tsx filename="app/api/profile/route.ts" switcher
import { NextRequest, NextResponse } from 'next/server'
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
export async function GET(req: NextRequest): Promise<NextResponse> {
// Verify the user's session
const session = await verifySession()
// If no session exists, return a 401 and render unauthorized.tsx
if (!session) {
unauthorized()
}
// Fetch data
// ...
}
```
```jsx filename="app/api/profile/route.js" switcher
import { verifySession } from '@/app/lib/dal'
import { unauthorized } from 'next/navigation'
export async function GET() {
const session = await verifySession()
// If the user is not authenticated, return a 401 and render unauthorized.tsx
if (!session) {
unauthorized()
}
// Fetch data
// ...
}
```
## Version History
| Version | Changes |
| --------- | -------------------------- |
| `v15.1.0` | `unauthorized` introduced. |
@@ -0,0 +1,98 @@
---
title: unstable_cache
description: API Reference for the unstable_cache function.
---
> **Note:**
> This API has been replaced by [`use cache`](/docs/app/api-reference/directives/use-cache) in Next.js 16.
> We recommend opting into [Cache Components](/docs/app/getting-started/caching) and replacing `unstable_cache` with the `use cache` directive.
`unstable_cache` allows you to cache the results of expensive operations, like database queries, and reuse them across multiple requests.
```jsx
import { getUser } from './data';
import { unstable_cache } from 'next/cache';
const getCachedUser = unstable_cache(
async (id) => getUser(id),
['my-app-user']
);
export default async function Component({ userID }) {
const user = await getCachedUser(userID);
...
}
```
> **Good to know**:
>
> - Accessing uncached data sources such as `headers` or `cookies` inside a cache scope is not supported. If you need this data inside a cached function use `headers` outside of the cached function and pass the required uncached data in as an argument.
> - This API uses Next.js' built-in cache to persist the result across requests and deployments. See [Caching and Revalidating](/docs/app/getting-started/caching).
## Parameters
```jsx
const data = unstable_cache(fetchData, keyParts, options)()
```
- `fetchData`: This is an asynchronous function that fetches the data you want to cache. It must be a function that returns a `Promise`.
- `keyParts`: This is an extra array of keys that further adds identification to the cache. By default, `unstable_cache` already uses the arguments and the stringified version of your function as the cache key. It is optional in most cases; the only time you need to use it is when you use external variables without passing them as parameters. However, it is important to add closures used within the function if you do not pass them as parameters.
- `options`: This is an object that controls how the cache behaves. It can contain the following properties:
- `tags`: An array of tags that can be used to control cache invalidation. Next.js will not use this to uniquely identify the function.
- `revalidate`: The number of seconds after which the cache should be revalidated. Omit or pass `false` to cache indefinitely or until matching `revalidateTag()` or `revalidatePath()` methods are called.
## Returns
`unstable_cache` returns a function that when invoked, returns a Promise that resolves to the cached data. If the data is not in the cache, the provided function will be invoked, and its result will be cached and returned.
## Example
```tsx filename="app/page.tsx" switcher
import { unstable_cache } from 'next/cache'
export default async function Page({
params,
}: {
params: Promise<{ userId: string }>
}) {
const { userId } = await params
const getCachedUser = unstable_cache(
async () => {
return { id: userId }
},
[userId], // add the user ID to the cache key
{
tags: ['users'],
revalidate: 60,
}
)
//...
}
```
```jsx filename="app/page.jsx" switcher
import { unstable_cache } from 'next/cache';
export default async function Page({ params } }) {
const { userId } = await params
const getCachedUser = unstable_cache(
async () => {
return { id: userId };
},
[userId], // add the user ID to the cache key
{
tags: ["users"],
revalidate: 60,
}
);
//...
}
```
## Version History
| Version | Changes |
| --------- | ---------------------------- |
| `v14.0.0` | `unstable_cache` introduced. |
@@ -0,0 +1,47 @@
---
title: unstable_noStore
description: API Reference for the unstable_noStore function.
version: legacy
---
**In version 15, we recommend using [`connection`](/docs/app/api-reference/functions/connection) instead of `unstable_noStore`.**
`unstable_noStore` can be used to declaratively opt out of prerendering and indicate a particular component should not be cached.
```jsx
import { unstable_noStore as noStore } from 'next/cache';
export default async function ServerComponent() {
noStore();
const result = await db.query(...);
...
}
```
> **Good to know**:
>
> - `unstable_noStore` is equivalent to `cache: 'no-store'` on a `fetch`
> - `unstable_noStore` is preferred over `export const dynamic = 'force-dynamic'` as it is more granular and can be used on a per-component basis
- Using `unstable_noStore` inside [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) will not opt out of static generation. Instead, it will defer to the cache configuration to determine whether to cache the result or not.
## Usage
If you prefer not to pass additional options to `fetch`, like `cache: 'no-store'`, `next: { revalidate: 0 }` or in cases where `fetch` is not available, you can use `noStore()` as a replacement for all of these use cases.
```jsx
import { unstable_noStore as noStore } from 'next/cache';
export default async function ServerComponent() {
noStore();
const result = await db.query(...);
...
}
```
## Version History
| Version | Changes |
| --------- | ----------------------------------------------- |
| `v15.0.0` | `unstable_noStore` deprecated for `connection`. |
| `v14.0.0` | `unstable_noStore` introduced. |
@@ -0,0 +1,65 @@
---
title: unstable_rethrow
description: API Reference for the unstable_rethrow function.
version: unstable
---
`unstable_rethrow` can be used to avoid catching internal errors thrown by Next.js when attempting to handle errors thrown in your application code.
For example, calling the `notFound` function will throw an internal Next.js error and render the [`not-found.js`](/docs/app/api-reference/file-conventions/not-found) component. However, if used inside the `try` block of a `try/catch` statement, the error will be caught, preventing `not-found.js` from rendering:
```tsx filename="@/app/ui/component.tsx"
import { notFound } from 'next/navigation'
export default async function Page() {
try {
const post = await fetch('https://.../posts/1').then((res) => {
if (res.status === 404) notFound()
if (!res.ok) throw new Error(res.statusText)
return res.json()
})
} catch (err) {
console.error(err)
}
}
```
You can use `unstable_rethrow` API to re-throw the internal error and continue with the expected behavior:
```tsx filename="@/app/ui/component.tsx"
import { notFound, unstable_rethrow } from 'next/navigation'
export default async function Page() {
try {
const post = await fetch('https://.../posts/1').then((res) => {
if (res.status === 404) notFound()
if (!res.ok) throw new Error(res.statusText)
return res.json()
})
} catch (err) {
unstable_rethrow(err)
console.error(err)
}
}
```
The following Next.js APIs rely on throwing an error which should be rethrown and handled by Next.js itself:
- [`notFound()`](/docs/app/api-reference/functions/not-found)
- [`redirect()`](/docs/app/guides/redirecting#redirect-function)
- [`permanentRedirect()`](/docs/app/guides/redirecting#permanentredirect-function)
If a route segment is marked to throw an error unless it's static, a Request-time API call will also throw an error that should similarly not be caught by the developer. Note that Partial Prerendering (PPR) affects this behavior as well. These APIs are:
- [`cookies`](/docs/app/api-reference/functions/cookies)
- [`headers`](/docs/app/api-reference/functions/headers)
- [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)
- `fetch(..., { cache: 'no-store' })`
- `fetch(..., { next: { revalidate: 0 } })`
> **Good to know**:
>
> - This method should be called at the top of the catch block, passing the error object as its only argument. It can also be used within a `.catch` handler of a promise.
> - You may be able to avoid using `unstable_rethrow` if you encapsulate your API calls that throw and let the **caller** handle the exception.
> - Only use `unstable_rethrow` if your caught exceptions may include both application errors and framework-controlled exceptions (like `redirect()` or `notFound()`).
> - Any resource cleanup (like clearing intervals, timers, etc) would have to either happen prior to the call to `unstable_rethrow` or within a `finally` block.
@@ -0,0 +1,153 @@
---
title: updateTag
description: API Reference for the updateTag function.
---
`updateTag` allows you to update cached data on-demand for a specific cache tag from within [Server Actions](/docs/app/getting-started/mutating-data).
This function is designed for **read-your-own-writes** scenarios, where a user makes a change (like creating a post), and the UI immediately shows the change, rather than stale data.
## Usage
`updateTag` can **only** be called from within [Server Actions](/docs/app/getting-started/mutating-data). It cannot be used in Route Handlers, Client Components, or any other context.
If you need to invalidate cache tags in Route Handlers or other contexts, use [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) instead.
> **Good to know**: `updateTag` immediately expires the cached data for the specified tag. The next request will wait to fetch fresh data rather than serving stale content from the cache, ensuring users see their changes immediately.
## Parameters
```tsx
updateTag(tag: string): void;
```
- `tag`: A string representing the cache tag associated with the data you want to update. Must not exceed 256 characters. This value is case-sensitive.
Tags must first be assigned to cached data. You can do this in two ways:
- Using the [`next.tags`](/docs/app/api-reference/functions/fetch) option with `fetch` for caching external API requests:
```tsx
fetch(url, { next: { tags: ['posts'] } })
```
- Using [`cacheTag`](/docs/app/api-reference/functions/cacheTag) inside cached functions or components with the `'use cache'` directive:
```tsx
import { cacheTag } from 'next/cache'
async function getData() {
'use cache'
cacheTag('posts')
// ...
}
```
## Returns
`updateTag` does not return a value.
## Differences from revalidateTag
While both `updateTag` and `revalidateTag` invalidate cached data, they serve different purposes:
- **`updateTag`**:
- Can only be used in Server Actions
- Next request waits for fresh data (no stale content served)
- Designed for read-your-own-writes scenarios
- **`revalidateTag`**:
- Can be used in Server Actions and Route Handlers
- With `profile="max"` (recommended): Serves cached data while fetching fresh data in the background (stale-while-revalidate)
- With custom profile: Can be configured to any cache life profile for advanced usage
- Without profile: legacy behavior which is equivalent to `updateTag`
## Examples
### Server Action with Read-Your-Own-Writes
```ts filename="app/actions.ts" switcher
'use server'
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')
// Create the post in your database
const post = await db.post.create({
data: { title, content },
})
// Invalidate cache tags so the new post is immediately visible
// 'posts' tag: affects any page that displays a list of posts
updateTag('posts')
// 'post-{id}' tag: affects the individual post detail page
updateTag(`post-${post.id}`)
// Redirect to the new post - user will see fresh data, not cached
redirect(`/posts/${post.id}`)
}
```
```js filename="app/actions.js" switcher
'use server'
import { updateTag } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData) {
const title = formData.get('title')
const content = formData.get('content')
// Create the post in your database
const post = await db.post.create({
data: { title, content },
})
// Invalidate cache tags so the new post is immediately visible
// 'posts' tag: affects any page that displays a list of posts
updateTag('posts')
// 'post-{id}' tag: affects the individual post detail page
updateTag(`post-${post.id}`)
// Redirect to the new post - user will see fresh data, not cached
redirect(`/posts/${post.id}`)
}
```
### Error when used outside Server Actions
```ts filename="app/api/posts/route.ts" switcher
import { updateTag } from 'next/cache'
export async function POST() {
// This will throw an error
updateTag('posts')
// Error: updateTag can only be called from within a Server Action
// Use revalidateTag instead in Route Handlers
revalidateTag('posts', 'max')
}
```
## When to use updateTag
Use `updateTag` when:
- You're in a Server Action
- You need immediate cache invalidation for read-your-own-writes
- You want to ensure the next request sees updated data
Use `revalidateTag` instead when:
- You're in a Route Handler or other non-action context
- You want stale-while-revalidate semantics
- You're building a webhook or API endpoint for cache invalidation
## Related
- [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) - For invalidating tags in Route Handlers
- [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) - For invalidating specific paths
@@ -0,0 +1,242 @@
---
title: useLinkStatus
description: API Reference for the useLinkStatus hook.
related:
title: Next Steps
description: Learn more about the features mentioned in this page by reading the API Reference.
links:
- app/api-reference/components/link
- app/api-reference/file-conventions/loading
---
The `useLinkStatus` hook lets you track the **pending** state of a `<Link>`. Use it for subtle, inline feedback, for example a shimmer effect over the clicked link, while navigation completes. Prefer route-level fallbacks with `loading.js`, and prefetching for instant transitions.
`useLinkStatus` is useful when:
- [Prefetching](/docs/app/getting-started/linking-and-navigating#prefetching) is disabled or in progress meaning navigation is blocked.
- The destination route is dynamic **and** doesn't include a [`loading.js`](/docs/app/api-reference/file-conventions/loading) file that would allow an instant navigation.
```tsx filename="app/hint.tsx" switcher
'use client'
import Link from 'next/link'
import { useLinkStatus } from 'next/link'
function Hint() {
const { pending } = useLinkStatus()
return (
<span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />
)
}
export default function Header() {
return (
<header>
<Link href="/dashboard" prefetch={false}>
<span className="label">Dashboard</span> <Hint />
</Link>
</header>
)
}
```
```jsx filename="app/hint.js" switcher
'use client'
import Link from 'next/link'
import { useLinkStatus } from 'next/link'
function Hint() {
const { pending } = useLinkStatus()
return (
<span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />
)
}
export default function Header() {
return (
<header>
<Link href="/dashboard" prefetch={false}>
<span className="label">Dashboard</span> <Hint />
</Link>
</header>
)
}
```
> **Good to know**:
>
> - `useLinkStatus` must be used within a descendant component of a `Link` component
> - The hook is most useful when `prefetch={false}` is set on the `Link` component
> - If the linked route has been prefetched, the pending state will be skipped
> - When clicking multiple links in quick succession, only the last link's pending state is shown
> - This hook is not supported in the Pages Router and always returns `{ pending: false }`
> - Inline indicators can easily introduce layout shifts. Prefer a fixed-size, always-rendered hint element and toggle its opacity, or use an animation.
## You might not need `useLinkStatus`
Before adding inline feedback, consider if:
- The destination is static and prefetched in production, so the pending phase may be skipped.
- The route has a `loading.js` file, enabling instant transitions with a route-level fallback.
Navigation is typically fast. Use `useLinkStatus` as a quick patch when you identify a slow transition, then iterate to fix the root cause with prefetching or a `loading.js` fallback.
## Parameters
```tsx
const { pending } = useLinkStatus()
```
`useLinkStatus` does not take any parameters.
## Returns
`useLinkStatus` returns an object with a single property:
| Property | Type | Description |
| -------- | ------- | -------------------------------------------- |
| pending | boolean | `true` before history updates, `false` after |
## Example
### Inline link hint
Add a subtle, fixed-size hint that doesnt affect layout to confirm a click when prefetching hasnt completed.
```tsx filename="app/components/loading-indicator.tsx" switcher
'use client'
import { useLinkStatus } from 'next/link'
export default function LoadingIndicator() {
const { pending } = useLinkStatus()
return (
<span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />
)
}
```
```jsx filename="app/components/loading-indicator.js" switcher
'use client'
import { useLinkStatus } from 'next/link'
export default function LoadingIndicator() {
const { pending } = useLinkStatus()
return (
<span aria-hidden className={`link-hint ${pending ? 'is-pending' : ''}`} />
)
}
```
```tsx filename="app/shop/layout.tsx" switcher
import Link from 'next/link'
import LoadingIndicator from './components/loading-indicator'
const links = [
{ href: '/shop/electronics', label: 'Electronics' },
{ href: '/shop/clothing', label: 'Clothing' },
{ href: '/shop/books', label: 'Books' },
]
function Menubar() {
return (
<div>
{links.map((link) => (
<Link key={link.label} href={link.href}>
<span className="label">{link.label}</span> <LoadingIndicator />
</Link>
))}
</div>
)
}
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Menubar />
{children}
</div>
)
}
```
```jsx filename="app/shop/layout.js" switcher
import Link from 'next/link'
import LoadingIndicator from './components/loading-indicator'
const links = [
{ href: '/shop/electronics', label: 'Electronics' },
{ href: '/shop/clothing', label: 'Clothing' },
{ href: '/shop/books', label: 'Books' },
]
function Menubar() {
return (
<div>
{links.map((link) => (
<Link key={link.label} href={link.href}>
<span className="label">{link.label}</span> <LoadingIndicator />
</Link>
))}
</div>
)
}
export default function Layout({ children }) {
return (
<div>
<Menubar />
{children}
</div>
)
}
```
## Gracefully handling fast navigation
If the navigation to a new route is fast, users may see an unnecessary flash of the hint. One way to improve the user experience and only show the hint when the navigation takes time to complete is to add an initial animation delay (e.g. 100ms) and start the animation as invisible (e.g. `opacity: 0`).
```css filename="app/styles/global.css"
.link-hint {
display: inline-block;
width: 0.6em;
height: 0.6em;
margin-left: 0.25rem;
border-radius: 9999px;
background: currentColor;
opacity: 0;
visibility: hidden; /* reserve space without showing the hint */
}
.link-hint.is-pending {
/* Animation 1: fade in after 100ms and keep final opacity */
/* Animation 2: subtle pulsing while pending */
visibility: visible;
animation-name: fadeIn, pulse;
animation-duration: 200ms, 1s;
/* Appear only if navigation actually takes time */
animation-delay: 100ms, 100ms;
animation-timing-function: ease, ease-in-out;
animation-iteration-count: 1, infinite;
animation-fill-mode: forwards, none;
}
@keyframes fadeIn {
to {
opacity: 0.35;
}
}
@keyframes pulse {
50% {
opacity: 0.15;
}
}
```
## Version History
| Version | Changes |
| --------- | --------------------------- |
| `v15.3.0` | `useLinkStatus` introduced. |
@@ -0,0 +1,73 @@
---
title: useParams
description: API Reference for the useParams hook.
---
`useParams` is a **Client Component** hook that lets you read a route's [dynamic params](/docs/app/api-reference/file-conventions/dynamic-routes) filled in by the current URL.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useParams } from 'next/navigation'
export default function ExampleClientComponent() {
const params = useParams<{ tag: string; item: string }>()
// Route -> /shop/[tag]/[item]
// URL -> /shop/shoes/nike-air-max-97
// `params` -> { tag: 'shoes', item: 'nike-air-max-97' }
console.log(params)
return '...'
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useParams } from 'next/navigation'
export default function ExampleClientComponent() {
const params = useParams()
// Route -> /shop/[tag]/[item]
// URL -> /shop/shoes/nike-air-max-97
// `params` -> { tag: 'shoes', item: 'nike-air-max-97' }
console.log(params)
return '...'
}
```
## Parameters
```tsx
const params = useParams()
```
`useParams` does not take any parameters.
## Returns
`useParams` returns an object containing the current route's filled in [dynamic parameters](/docs/app/api-reference/file-conventions/dynamic-routes).
- Each property in the object is an active dynamic segment.
- The properties name is the segment's name, and the properties value is what the segment is filled in with.
- The properties value will either be a `string` or array of `string`'s depending on the [type of dynamic segment](/docs/app/api-reference/file-conventions/dynamic-routes).
- If the route contains no dynamic parameters, `useParams` returns an empty object.
- If used in Pages Router, `useParams` will return `null` on the initial render and updates with properties following the rules above once the router is ready.
For example:
| Route | URL | `useParams()` |
| ------------------------------- | ----------- | ------------------------- |
| `app/shop/page.js` | `/shop` | `{}` |
| `app/shop/[slug]/page.js` | `/shop/1` | `{ slug: '1' }` |
| `app/shop/[tag]/[item]/page.js` | `/shop/1/2` | `{ tag: '1', item: '2' }` |
| `app/shop/[...slug]/page.js` | `/shop/1/2` | `{ slug: ['1', '2'] }` |
## Version History
| Version | Changes |
| --------- | ----------------------- |
| `v13.3.0` | `useParams` introduced. |
@@ -0,0 +1,156 @@
---
title: usePathname
description: API Reference for the usePathname hook.
---
`usePathname` is a **Client Component** hook that lets you read the current URL's **pathname**.
> **Good to know**: When [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled `usePathname` may require a `Suspense` boundary around it if your route has a dynamic param. If you use `generateStaticParams` the `Suspense` boundary is optional
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { usePathname } from 'next/navigation'
export default function ExampleClientComponent() {
const pathname = usePathname()
return <p>Current pathname: {pathname}</p>
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { usePathname } from 'next/navigation'
export default function ExampleClientComponent() {
const pathname = usePathname()
return <p>Current pathname: {pathname}</p>
}
```
`usePathname` intentionally requires using a [Client Component](/docs/app/getting-started/server-and-client-components). It's important to note Client Components are not a de-optimization. They are an integral part of the [Server Components](/docs/app/getting-started/server-and-client-components) architecture.
For example, a Client Component with `usePathname` will be rendered into HTML on the initial page load. When navigating to a new route, this component does not need to be re-fetched. Instead, the component is downloaded once (in the client JavaScript bundle), and re-renders based on the current state.
> **Good to know**:
>
> - Reading the current URL from a [Server Component](/docs/app/getting-started/server-and-client-components) is not supported. This design is intentional to support layout state being preserved across page navigations.
> - If your page is being statically prerendered and your app has [rewrites](/docs/app/api-reference/config/next-config-js/rewrites) in `next.config` or a [Proxy](/docs/app/api-reference/file-conventions/proxy) file, reading the pathname with `usePathname()` can result in hydration mismatch errors—because the initial value comes from the server and may not match the actual browser pathname after routing. See our [example](#avoid-hydration-mismatch-with-rewrites) for a way to mitigate this issue.
<details>
<summary>Compatibility with Pages Router</summary>
If you have components that use `usePathname` and they are imported into routes within the Pages Router, be aware that `usePathname` may return `null` if the router is not yet initialized. This can occur in cases such as [fallback routes](/docs/pages/api-reference/functions/get-static-paths#fallback-true) or during [Automatic Static Optimization](https://nextjs.org/docs/pages/building-your-application/rendering/static#automatic-static-optimization) in the Pages Router.
To enhance compatibility between routing systems, if your project contains both an `app` and a `pages` directory, Next.js will automatically adjust the return type of `usePathname`.
</details>
## Parameters
```tsx
const pathname = usePathname()
```
`usePathname` does not take any parameters.
## Returns
`usePathname` returns a string of the current URL's pathname. For example:
| URL | Returned value |
| ------------------- | --------------------- |
| `/` | `'/'` |
| `/dashboard` | `'/dashboard'` |
| `/dashboard?v=2` | `'/dashboard'` |
| `/blog/hello-world` | `'/blog/hello-world'` |
## Examples
### Do something in response to a route change
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
function ExampleClientComponent() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
// Do something here...
}, [pathname, searchParams])
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
function ExampleClientComponent() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
// Do something here...
}, [pathname, searchParams])
}
```
### Avoid hydration mismatch with rewrites
When a page is prerendered, the HTML is generated for the source pathname. If the page is then reached through a rewrite using `next.config` or `Proxy`, the browser URL may differ, and `usePathname()` will read the rewritten pathname on the client.
To avoid hydration mismatches, design the UI so that only a small, isolated part depends on the client pathname. Render a stable fallback on the server and update that part after mount.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useEffect, useState } from 'react'
import { usePathname } from 'next/navigation'
export default function PathnameBadge() {
const pathname = usePathname()
const [clientPathname, setClientPathname] = useState('')
useEffect(() => {
setClientPathname(pathname)
}, [pathname])
return (
<p>
Current pathname: <span>{clientPathname}</span>
</p>
)
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useEffect, useState } from 'react'
import { usePathname } from 'next/navigation'
export default function PathnameBadge() {
const pathname = usePathname()
const [clientPathname, setClientPathname] = useState('')
useEffect(() => {
setClientPathname(pathname)
}, [pathname])
return (
<p>
Current pathname: <span>{clientPathname}</span>
</p>
)
}
```
| Version | Changes |
| --------- | ------------------------- |
| `v13.0.0` | `usePathname` introduced. |
@@ -0,0 +1,250 @@
---
title: useReportWebVitals
description: API Reference for the useReportWebVitals function.
---
{/* 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. */}
The `useReportWebVitals` hook allows you to report [Core Web Vitals](https://web.dev/vitals/), and can be used in combination with your analytics service.
New functions passed to `useReportWebVitals` are called with the available metrics up to that point. To prevent reporting duplicated data, ensure that the callback function reference does not change (as shown in the code examples below).
<PagesOnly>
```jsx filename="pages/_app.js"
import { useReportWebVitals } from 'next/web-vitals'
const logWebVitals = (metric) => {
console.log(metric)
}
function MyApp({ Component, pageProps }) {
useReportWebVitals(logWebVitals)
return <Component {...pageProps} />
}
```
</PagesOnly>
<AppOnly>
```jsx filename="app/_components/web-vitals.js"
'use client'
import { useReportWebVitals } from 'next/web-vitals'
const logWebVitals = (metric) => {
console.log(metric)
}
export function WebVitals() {
useReportWebVitals(logWebVitals)
return null
}
```
```jsx filename="app/layout.js"
import { WebVitals } from './_components/web-vitals'
export default function Layout({ children }) {
return (
<html>
<body>
<WebVitals />
{children}
</body>
</html>
)
}
```
> Since the `useReportWebVitals` hook requires the `'use client'` directive, the most performant approach is to create a separate component that the root layout imports. This confines the client boundary exclusively to the `WebVitals` component.
</AppOnly>
## useReportWebVitals
The `metric` object passed as the hook's argument consists of a number of properties:
- `id`: Unique identifier for the metric in the context of the current page load
- `name`: The name of the performance metric. Possible values include names of [Web Vitals](#web-vitals) metrics (TTFB, FCP, LCP, FID, CLS) specific to a web application.
- `delta`: The difference between the current value and the previous value of the metric. The value is typically in milliseconds and represents the change in the metric's value over time.
- `entries`: An array of [Performance Entries](https://developer.mozilla.org/docs/Web/API/PerformanceEntry) associated with the metric. These entries provide detailed information about the performance events related to the metric.
- `navigationType`: Indicates the navigation type that triggered metric collection. Values are derived from [PerformanceNavigationTiming.type](https://developer.mozilla.org/docs/Web/API/PerformanceNavigationTiming/type) and may include `"navigate"`, `"reload"`, `"prerender"`, `"back-forward"` (normalized from `"back_forward"`), `"back-forward-cache"` (BFCache restore), and `"restore"` (page restored after discard).
- `rating`: A qualitative rating of the metric value, providing an assessment of the performance. Possible values are `"good"`, `"needs-improvement"`, and `"poor"`. The rating is typically determined by comparing the metric value against predefined thresholds that indicate acceptable or suboptimal performance.
- `value`: The actual value or duration of the performance entry, typically in milliseconds. The value provides a quantitative measure of the performance aspect being tracked by the metric. The source of the value depends on the specific metric being measured and can come from various [Performance API](https://developer.mozilla.org/docs/Web/API/Performance_API)s.
## Web Vitals
[Web Vitals](https://web.dev/vitals/) are a set of useful metrics that aim to capture the user
experience of a web page. The following web vitals are all included:
- [Time to First Byte](https://developer.mozilla.org/docs/Glossary/Time_to_first_byte) (TTFB)
- [First Contentful Paint](https://developer.mozilla.org/docs/Glossary/First_contentful_paint) (FCP)
- [Largest Contentful Paint](https://web.dev/lcp/) (LCP)
- [First Input Delay](https://web.dev/fid/) (FID)
- [Cumulative Layout Shift](https://web.dev/cls/) (CLS)
- [Interaction to Next Paint](https://web.dev/inp/) (INP)
You can handle all the results of these metrics using the `name` property.
<PagesOnly>
```jsx filename="pages/_app.js"
import { useReportWebVitals } from 'next/web-vitals'
const handleWebVitals = (metric) => {
switch (metric.name) {
case 'FCP': {
// handle FCP results
}
case 'LCP': {
// handle LCP results
}
// ...
}
}
function MyApp({ Component, pageProps }) {
useReportWebVitals(handleWebVitals)
return <Component {...pageProps} />
}
```
</PagesOnly>
<AppOnly>
```tsx filename="app/components/web-vitals.tsx" switcher
'use client'
import { useReportWebVitals } from 'next/web-vitals'
type ReportWebVitalsCallback = Parameters<typeof useReportWebVitals>[0]
const handleWebVitals: ReportWebVitalsCallback = (metric) => {
switch (metric.name) {
case 'FCP': {
// handle FCP results
}
case 'LCP': {
// handle LCP results
}
// ...
}
}
export function WebVitals() {
useReportWebVitals(handleWebVitals)
}
```
```jsx filename="app/components/web-vitals.js" switcher
'use client'
import { useReportWebVitals } from 'next/web-vitals'
const handleWebVitals = (metric) => {
switch (metric.name) {
case 'FCP': {
// handle FCP results
}
case 'LCP': {
// handle LCP results
}
// ...
}
}
export function WebVitals() {
useReportWebVitals(handleWebVitals)
}
```
</AppOnly>
<PagesOnly>
## Custom Metrics
In addition to the core metrics listed above, there are some additional custom metrics that
measure the time it takes for the page to hydrate and render:
- `Next.js-hydration`: Length of time it takes for the page to start and finish hydrating (in ms)
- `Next.js-route-change-to-render`: Length of time it takes for a page to start rendering after a
route change (in ms)
- `Next.js-render`: Length of time it takes for a page to finish render after a route change (in ms)
You can handle all the results of these metrics separately:
```jsx filename="pages/_app.js"
import { useReportWebVitals } from 'next/web-vitals'
function handleCustomMetrics(metric) {
switch (metric.name) {
case 'Next.js-hydration':
// handle hydration results
break
case 'Next.js-route-change-to-render':
// handle route-change to render results
break
case 'Next.js-render':
// handle render results
break
default:
break
}
}
function MyApp({ Component, pageProps }) {
useReportWebVitals(handleCustomMetrics)
return <Component {...pageProps} />
}
```
These metrics work in all browsers that support the [User Timing API](https://caniuse.com/#feat=user-timing).
</PagesOnly>
## Sending results to external systems
You can send results to any endpoint to measure and track
real user performance on your site. For example:
```js
function postWebVitals(metric) {
const body = JSON.stringify(metric)
const url = 'https://example.com/analytics'
// Use `navigator.sendBeacon()` if available, falling back to `fetch()`.
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body)
} else {
fetch(url, { body, method: 'POST', keepalive: true })
}
}
useReportWebVitals(postWebVitals)
```
> **Good to know**: If you use [Google Analytics](https://analytics.google.com/analytics/web/), using the
> `id` value can allow you to construct metric distributions manually (to calculate percentiles,
> etc.)
> ```js
> useReportWebVitals(metric => {
> // Use `window.gtag` if you initialized Google Analytics as this example:
> // https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics
> window.gtag('event', metric.name, {
> value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), // values must be integers
> event_label: metric.id, // id unique to current page load
> non_interaction: true, // avoids affecting bounce rate.
> });
> }
> ```
>
> Read more about [sending results to Google Analytics](https://github.com/GoogleChrome/web-vitals#send-the-results-to-google-analytics).
@@ -0,0 +1,164 @@
---
title: useRouter
description: API reference for the useRouter hook.
---
The `useRouter` hook allows you to programmatically change routes inside [Client Components](/docs/app/getting-started/server-and-client-components).
> **Recommendation:** Use the [`<Link>` component](/docs/app/api-reference/components/link) for navigation unless you have a specific requirement for using `useRouter`.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
```
## `useRouter()`
- `router.push(href: string, { scroll: boolean, transitionTypes: string[] })`: Perform a client-side navigation to the provided route. Adds a new entry into the [browser's history stack](https://developer.mozilla.org/docs/Web/API/History_API). The optional `transitionTypes` are passed to [`React.addTransitionType`](https://react.dev/reference/react/addTransitionType) inside the navigation Transition.
- `router.replace(href: string, { scroll: boolean, transitionTypes: string[] })`: Perform a client-side navigation to the provided route without adding a new entry into the browsers history stack. The optional `transitionTypes` are passed to [`React.addTransitionType`](https://react.dev/reference/react/addTransitionType) inside the navigation Transition.
- `router.refresh()`: Refresh the current route. Making a new request to the server, re-fetching data requests, and re-rendering Server Components. The client will merge the updated React Server Component payload without losing unaffected client-side React (e.g. `useState`) or browser state (e.g. scroll position). This clears the [Client Cache](/docs/app/glossary#client-cache) for the current route, but does **not** invalidate the server-side cache. Use [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) to invalidate server-side cached data.
- `router.prefetch(href: string, options?: { onInvalidate?: () => void })`: [Prefetch](/docs/app/getting-started/linking-and-navigating#prefetching) the provided route for faster client-side transitions. The optional `onInvalidate` callback is called when the [prefetched data becomes stale](/docs/app/guides/prefetching#extending-or-ejecting-link).
- `router.back()`: Navigate back to the previous route in the browsers history stack.
- `router.forward()`: Navigate forwards to the next page in the browsers history stack.
> **Good to know**:
>
> - You must not send untrusted or unsanitized URLs to `router.push` or `router.replace`, as this can open your site to cross-site scripting (XSS) vulnerabilities. For example, `javascript:` URLs sent to `router.push` or `router.replace` will be executed in the context of your page.
> - The `<Link>` component automatically prefetch routes as they become visible in the viewport.
> - `refresh()` could re-produce the same result if fetch requests are cached. Other Request-time APIs like `cookies` and `headers` could also change the response.
> - The `onInvalidate` callback is called at most once per prefetch request. It signals when you may want to trigger a new prefetch for updated route data.
### Migrating from `next/router`
- The `useRouter` hook should be imported from `next/navigation` and not `next/router` when using the App Router
- The `pathname` string has been removed and is replaced by [`usePathname()`](/docs/app/api-reference/functions/use-pathname)
- The `query` object has been removed and is replaced by [`useSearchParams()`](/docs/app/api-reference/functions/use-search-params)
- `router.events` has been replaced. [See below.](#router-events)
[View the full migration guide](/docs/app/guides/migrating/app-router-migration).
## Examples
### Router events
You can listen for page changes by composing other Client Component hooks like `usePathname` and `useSearchParams`.
```jsx filename="app/components/navigation-events.js"
'use client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'
export function NavigationEvents() {
const pathname = usePathname()
const searchParams = useSearchParams()
useEffect(() => {
const url = `${pathname}?${searchParams}`
console.log(url)
// You can now use the current URL
// ...
}, [pathname, searchParams])
return '...'
}
```
Which can be imported into a layout.
```jsx filename="app/layout.js" highlight={2,10-12}
import { Suspense } from 'react'
import { NavigationEvents } from './components/navigation-events'
export default function Layout({ children }) {
return (
<html lang="en">
<body>
{children}
<Suspense fallback={null}>
<NavigationEvents />
</Suspense>
</body>
</html>
)
}
```
> **Good to know**: `<NavigationEvents>` is wrapped in a [`Suspense` boundary](/docs/app/api-reference/file-conventions/loading#examples) because[`useSearchParams()`](/docs/app/api-reference/functions/use-search-params) causes client-side rendering up to the closest `Suspense` boundary during [prerendering](/docs/app/glossary#prerendering). [Learn more](/docs/app/api-reference/functions/use-search-params#behavior).
### Disabling scroll to top
By default, Next.js will scroll to the top of the page when navigating to a new route. You can disable this behavior by passing `scroll: false` to `router.push()` or `router.replace()`.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button
type="button"
onClick={() => router.push('/dashboard', { scroll: false })}
>
Dashboard
</button>
)
}
```
```jsx filename="app/example-client-component.jsx" switcher
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button
type="button"
onClick={() => router.push('/dashboard', { scroll: false })}
>
Dashboard
</button>
)
}
```
## Version History
| Version | Changes |
| --------- | ----------------------------------------------------------------- |
| `v15.4.0` | Optional `onInvalidate` callback for `router.prefetch` introduced |
| `v13.0.0` | `useRouter` from `next/navigation` introduced. |
@@ -0,0 +1,382 @@
---
title: useSearchParams
description: API Reference for the useSearchParams hook.
---
`useSearchParams` is a **Client Component** hook that lets you read the current URL's **query string**.
`useSearchParams` returns a **read-only** version of the [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) interface.
```tsx filename="app/dashboard/search-bar.tsx" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
return <>Search: {search}</>
}
```
```jsx filename="app/dashboard/search-bar.js" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
return <>Search: {search}</>
}
```
## Parameters
```tsx
const searchParams = useSearchParams()
```
`useSearchParams` does not take any parameters.
## Returns
`useSearchParams` returns a **read-only** version of the [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) interface, which includes utility methods for reading the URL's query string:
- [`URLSearchParams.get()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get): Returns the first value associated with the search parameter. For example:
| URL | `searchParams.get("a")` |
| -------------------- | --------------------------------------------------------------------------------------------------------------- |
| `/dashboard?a=1` | `'1'` |
| `/dashboard?a=` | `''` |
| `/dashboard?b=3` | `null` |
| `/dashboard?a=1&a=2` | `'1'` _- use [`getAll()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) to get all values_ |
- [`URLSearchParams.has()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has): Returns a boolean value indicating if the given parameter exists. For example:
| URL | `searchParams.has("a")` |
| ---------------- | ----------------------- |
| `/dashboard?a=1` | `true` |
| `/dashboard?b=3` | `false` |
- Learn more about other **read-only** methods of [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams), including the [`getAll()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll), [`keys()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/keys), [`values()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/values), [`entries()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/entries), [`forEach()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/forEach), and [`toString()`](https://developer.mozilla.org/docs/Web/API/URLSearchParams/toString).
> **Good to know**:
>
> - `useSearchParams` is a [Client Component](/docs/app/getting-started/server-and-client-components) hook and is **not supported** in [Server Components](/docs/app/getting-started/server-and-client-components) to prevent stale values during [partial rendering](/docs/app/getting-started/linking-and-navigating#client-side-transitions).
> - If you want to fetch data in a Server Component based on search params, it's often a better option to read the [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) of the corresponding Page. You can then pass it down by props to any component (Server or Client) within that Page.
> - If an application includes the `/pages` directory, `useSearchParams` will return `ReadonlyURLSearchParams | null`. The `null` value is for compatibility during migration since search params cannot be known during prerendering of a page that doesn't use `getServerSideProps`
## Behavior
### Prerendering
If a route is [prerendered](/docs/app/glossary#prerendering), calling `useSearchParams` will cause the Client Component tree up to the closest [`Suspense` boundary](/docs/app/api-reference/file-conventions/loading#examples) to be client-side rendered.
This allows a part of the route to be prerendered while the dynamic part that uses `useSearchParams` is client-side rendered.
We recommend wrapping the Client Component that uses `useSearchParams` in a `<Suspense/>` boundary. This will allow any Client Components above it to be prerendered and sent as part of initial HTML. [Example](/docs/app/api-reference/functions/use-search-params#prerendering).
For example:
```tsx filename="app/dashboard/search-bar.tsx" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// This will not be logged on the server during prerendering
console.log(search)
return <>Search: {search}</>
}
```
```jsx filename="app/dashboard/search-bar.js" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// This will not be logged on the server during prerendering
console.log(search)
return <>Search: {search}</>
}
```
```tsx filename="app/dashboard/page.tsx" switcher
import { Suspense } from 'react'
import SearchBar from './search-bar'
// This component passed as a fallback to the Suspense boundary
// will be rendered in place of the search bar in the initial HTML.
// When the value is available during React hydration the fallback
// will be replaced with the `<SearchBar>` component.
function SearchBarFallback() {
return <>placeholder</>
}
export default function Page() {
return (
<>
<nav>
<Suspense fallback={<SearchBarFallback />}>
<SearchBar />
</Suspense>
</nav>
<h1>Dashboard</h1>
</>
)
}
```
```jsx filename="app/dashboard/page.js" switcher
import { Suspense } from 'react'
import SearchBar from './search-bar'
// This component passed as a fallback to the Suspense boundary
// will be rendered in place of the search bar in the initial HTML.
// When the value is available during React hydration the fallback
// will be replaced with the `<SearchBar>` component.
function SearchBarFallback() {
return <>placeholder</>
}
export default function Page() {
return (
<>
<nav>
<Suspense fallback={<SearchBarFallback />}>
<SearchBar />
</Suspense>
</nav>
<h1>Dashboard</h1>
</>
)
}
```
> **Good to know**:
>
> - In development, routes are rendered on-demand, so `useSearchParams` doesn't suspend and things may appear to work without `Suspense`.
> - During production builds, a static page that calls `useSearchParams` from a Client Component must be wrapped in a `Suspense` boundary, otherwise the build fails with the [Missing Suspense boundary with useSearchParams](/docs/messages/missing-suspense-with-csr-bailout) error.
> - If you intend the route to be dynamically rendered, prefer using the [`connection`](/docs/app/api-reference/functions/connection) function first in a Server Component to wait for an incoming request, this excludes everything below from prerendering. See what makes a route dynamic in the [Dynamic Rendering guide](/docs/app/glossary#dynamic-rendering).
> - If you're already in a Server Component Page, consider using the [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) and passing the values to Client Components.
> - You can also pass the Page [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) directly to a Client Component and unwrap it with React's `use()`. Although this will suspend, so the Client Component should be wrapped with a `Suspense` boundary.
### Dynamic Rendering
If a route is dynamically rendered, `useSearchParams` will be available on the server during the initial server render of the Client Component.
For example:
```tsx filename="app/dashboard/search-bar.tsx" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// This will be logged on the server during the initial render
// and on the client on subsequent navigations.
console.log(search)
return <>Search: {search}</>
}
```
```jsx filename="app/dashboard/search-bar.js" switcher
'use client'
import { useSearchParams } from 'next/navigation'
export default function SearchBar() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
// This will be logged on the server during the initial render
// and on the client on subsequent navigations.
console.log(search)
return <>Search: {search}</>
}
```
```tsx filename="app/dashboard/page.tsx" switcher
import { connection } from 'next/server'
import SearchBar from './search-bar'
export default async function Page() {
await connection()
return (
<>
<nav>
<SearchBar />
</nav>
<h1>Dashboard</h1>
</>
)
}
```
```jsx filename="app/dashboard/page.js" switcher
import { connection } from 'next/server'
import SearchBar from './search-bar'
export default async function Page() {
await connection()
return (
<>
<nav>
<SearchBar />
</nav>
<h1>Dashboard</h1>
</>
)
}
```
> **Good to know**:
>
> - Previously, setting `export const dynamic = 'force-dynamic'` on the page was used to force dynamic rendering. Prefer using [`connection()`](/docs/app/api-reference/functions/connection) instead, as it semantically ties dynamic rendering to the incoming request.
### Server Components
#### Pages
To access search params in [Pages](/docs/app/api-reference/file-conventions/page) (Server Components), use the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop.
#### Layouts
Unlike Pages, [Layouts](/docs/app/api-reference/file-conventions/layout) (Server Components) **do not** receive the `searchParams` prop. This is because a shared layout is [not re-rendered during navigation](/docs/app/getting-started/linking-and-navigating#client-side-transitions) which could lead to stale `searchParams` between navigations. View [detailed explanation](/docs/app/api-reference/file-conventions/layout#query-params).
Instead, use the Page [`searchParams`](/docs/app/api-reference/file-conventions/page) prop or the [`useSearchParams`](/docs/app/api-reference/functions/use-search-params) hook in a Client Component, which is re-rendered on the client with the latest `searchParams`.
## Examples
### Updating `searchParams`
You can use [`useRouter`](/docs/app/api-reference/functions/use-router) or [`Link`](/docs/app/api-reference/components/link) to set new `searchParams`. After a navigation is performed, the current [`page.js`](/docs/app/api-reference/file-conventions/page) will receive an updated [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional).
```tsx filename="app/example-client-component.tsx" switcher
'use client'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString())
params.set(name, value)
return params.toString()
},
[searchParams]
)
return (
<>
<p>Sort By</p>
{/* using useRouter */}
<button
onClick={() => {
// <pathname>?sort=asc
router.push(pathname + '?' + createQueryString('sort', 'asc'))
}}
>
ASC
</button>
{/* using <Link> */}
<Link
href={
// <pathname>?sort=desc
pathname + '?' + createQueryString('sort', 'desc')
}
>
DESC
</Link>
</>
)
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
const createQueryString = useCallback(
(name, value) => {
const params = new URLSearchParams(searchParams)
params.set(name, value)
return params.toString()
},
[searchParams]
)
return (
<>
<p>Sort By</p>
{/* using useRouter */}
<button
onClick={() => {
// <pathname>?sort=asc
router.push(pathname + '?' + createQueryString('sort', 'asc'))
}}
>
ASC
</button>
{/* using <Link> */}
<Link
href={
// <pathname>?sort=desc
pathname + '?' + createQueryString('sort', 'desc')
}
>
DESC
</Link>
</>
)
}
```
## Version History
| Version | Changes |
| --------- | ----------------------------- |
| `v13.0.0` | `useSearchParams` introduced. |
@@ -0,0 +1,179 @@
---
title: useSelectedLayoutSegment
description: API Reference for the useSelectedLayoutSegment hook.
---
`useSelectedLayoutSegment` is a **Client Component** hook that lets you read the active route segment **one level below** the Layout it is called from.
It is useful for navigation UI, such as tabs inside a parent layout that change style depending on the active child segment.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function ExampleClientComponent() {
const segment = useSelectedLayoutSegment()
return <p>Active segment: {segment}</p>
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function ExampleClientComponent() {
const segment = useSelectedLayoutSegment()
return <p>Active segment: {segment}</p>
}
```
> **Good to know**:
>
> - Since `useSelectedLayoutSegment` is a [Client Component](/docs/app/getting-started/server-and-client-components) hook, and Layouts are [Server Components](/docs/app/getting-started/server-and-client-components) by default, `useSelectedLayoutSegment` is usually called via a Client Component that is imported into a Layout.
> - `useSelectedLayoutSegment` only returns the segment one level down. To return all active segments, see [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments)
> - For [catch-all](/docs/app/api-reference/file-conventions/dynamic-routes#catch-all-segments) routes, the matched segments are returned as a single joined string. For example, given `app/blog/[...slug]/page.js`, calling from `app/blog/layout.js` when visiting `/blog/a/b/c` returns `'a/b/c'`.
## Parameters
```tsx
const segment = useSelectedLayoutSegment(parallelRoutesKey?: string)
```
`useSelectedLayoutSegment` _optionally_ accepts a [`parallelRoutesKey`](/docs/app/api-reference/file-conventions/parallel-routes#with-useselectedlayoutsegments), which allows you to read the active route segment within that slot.
## Returns
`useSelectedLayoutSegment` returns a string of the active segment or `null` if one doesn't exist.
For example, given the Layouts and URLs below, the returned segment would be:
| Layout | Visited URL | Returned Segment |
| ------------------------- | ------------------------------ | ---------------- |
| `app/layout.js` | `/` | `null` |
| `app/layout.js` | `/dashboard` | `'dashboard'` |
| `app/dashboard/layout.js` | `/dashboard` | `null` |
| `app/dashboard/layout.js` | `/dashboard/settings` | `'settings'` |
| `app/dashboard/layout.js` | `/dashboard/analytics` | `'analytics'` |
| `app/dashboard/layout.js` | `/dashboard/analytics/monthly` | `'analytics'` |
For catch-all routes (`[...slug]`), the returned segment contains all matched path segments joined as a single string:
| Layout | Visited URL | Returned Segment |
| -------------------- | ------------- | ---------------- |
| `app/blog/layout.js` | `/blog/a/b/c` | `'a/b/c'` |
## Examples
### Creating an active link component
You can use `useSelectedLayoutSegment` to create an active link component that changes style depending on the active segment. For example, a featured posts list in the sidebar of a blog:
```tsx filename="app/blog/blog-nav-link.tsx" switcher
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
// This *client* component will be imported into a blog layout
export default function BlogNavLink({
slug,
children,
}: {
slug: string
children: React.ReactNode
}) {
// Navigating to `/blog/hello-world` will return 'hello-world'
// for the selected layout segment
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/blog/blog-nav-link.js" switcher
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
// This *client* component will be imported into a blog layout
export default function BlogNavLink({ slug, children }) {
// Navigating to `/blog/hello-world` will return 'hello-world'
// for the selected layout segment
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>
)
}
```
```tsx filename="app/blog/layout.tsx" switcher
// Import the Client Component into a parent Layout (Server Component)
import { BlogNavLink } from './blog-nav-link'
import getFeaturedPosts from './get-featured-posts'
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const featuredPosts = await getFeaturedPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<BlogNavLink slug={post.slug}>{post.title}</BlogNavLink>
</div>
))}
<div>{children}</div>
</div>
)
}
```
```jsx filename="app/blog/layout.js" switcher
// Import the Client Component into a parent Layout (Server Component)
import { BlogNavLink } from './blog-nav-link'
import getFeaturedPosts from './get-featured-posts'
export default async function Layout({ children }) {
const featuredPosts = await getFeaturedPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<BlogNavLink slug={post.slug}>{post.title}</BlogNavLink>
</div>
))}
<div>{children}</div>
</div>
)
}
```
## Version History
| Version | Changes |
| --------- | -------------------------------------- |
| `v13.0.0` | `useSelectedLayoutSegment` introduced. |
@@ -0,0 +1,85 @@
---
title: useSelectedLayoutSegments
description: API Reference for the useSelectedLayoutSegments hook.
---
`useSelectedLayoutSegments` is a **Client Component** hook that lets you read the active route segments **below** the Layout it is called from.
It is useful for creating UI in parent Layouts that need knowledge of active child segments such as breadcrumbs.
```tsx filename="app/example-client-component.tsx" switcher
'use client'
import { useSelectedLayoutSegments } from 'next/navigation'
export default function ExampleClientComponent() {
const segments = useSelectedLayoutSegments()
return (
<ul>
{segments.map((segment, index) => (
<li key={index}>{segment}</li>
))}
</ul>
)
}
```
```jsx filename="app/example-client-component.js" switcher
'use client'
import { useSelectedLayoutSegments } from 'next/navigation'
export default function ExampleClientComponent() {
const segments = useSelectedLayoutSegments()
return (
<ul>
{segments.map((segment, index) => (
<li key={index}>{segment}</li>
))}
</ul>
)
}
```
> **Good to know**:
>
> - Since `useSelectedLayoutSegments` is a [Client Component](/docs/app/getting-started/server-and-client-components) hook, and Layouts are [Server Components](/docs/app/getting-started/server-and-client-components) by default, `useSelectedLayoutSegments` is usually called via a Client Component that is imported into a Layout.
> - The returned segments include [Route Groups](/docs/app/api-reference/file-conventions/route-groups), which you might not want to be included in your UI. You can use the [`filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) array method to remove items that start with a bracket.
> - For [catch-all](/docs/app/api-reference/file-conventions/dynamic-routes#catch-all-segments) routes, the matched segments are returned as a single joined string within the array. For example, given `app/blog/[...slug]/page.js`, calling from `app/layout.js` when visiting `/blog/a/b/c` returns `['blog', 'a/b/c']`, not `['blog', 'a', 'b', 'c']`.
## Parameters
```tsx
const segments = useSelectedLayoutSegments(parallelRoutesKey?: string)
```
`useSelectedLayoutSegments` _optionally_ accepts a [`parallelRoutesKey`](/docs/app/api-reference/file-conventions/parallel-routes#with-useselectedlayoutsegments), which allows you to read the active route segment within that slot.
## Returns
`useSelectedLayoutSegments` returns an array of strings containing the active segments one level down from the layout the hook was called from. Or an empty array if none exist.
For example, given the Layouts and URLs below, the returned segments would be:
| Layout | Visited URL | Returned Segments |
| ------------------------- | --------------------- | --------------------------- |
| `app/layout.js` | `/` | `[]` |
| `app/layout.js` | `/dashboard` | `['dashboard']` |
| `app/layout.js` | `/dashboard/settings` | `['dashboard', 'settings']` |
| `app/dashboard/layout.js` | `/dashboard` | `[]` |
| `app/dashboard/layout.js` | `/dashboard/settings` | `['settings']` |
For catch-all routes (`[...slug]`), all matched path segments are returned as a single joined string within the array:
| Layout | Visited URL | Returned Segments |
| -------------------- | ------------- | ------------------- |
| `app/layout.js` | `/blog/a/b/c` | `['blog', 'a/b/c']` |
| `app/blog/layout.js` | `/blog/a/b/c` | `['a/b/c']` |
## Version History
| Version | Changes |
| --------- | --------------------------------------- |
| `v13.0.0` | `useSelectedLayoutSegments` introduced. |
@@ -0,0 +1,79 @@
---
title: userAgent
description: The userAgent helper extends the Web Request API with additional properties and methods to interact with the user agent object from the request.
---
{/* 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. */}
The `userAgent` helper extends the [Web Request API](https://developer.mozilla.org/docs/Web/API/Request) with additional properties and methods to interact with the user agent object from the request.
```ts filename="proxy.ts" switcher
import { NextRequest, NextResponse, userAgent } from 'next/server'
export function proxy(request: NextRequest) {
const url = request.nextUrl
const { device } = userAgent(request)
// device.type can be: 'mobile', 'tablet', 'console', 'smarttv',
// 'wearable', 'embedded', or undefined (for desktop browsers)
const viewport = device.type || 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}
```
```js filename="proxy.js" switcher
import { NextResponse, userAgent } from 'next/server'
export function proxy(request) {
const url = request.nextUrl
const { device } = userAgent(request)
// device.type can be: 'mobile', 'tablet', 'console', 'smarttv',
// 'wearable', 'embedded', or undefined (for desktop browsers)
const viewport = device.type || 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}
```
## `isBot`
A boolean indicating whether the request comes from a known bot.
## `browser`
An object containing information about the browser used in the request.
- `name`: A string representing the browser's name, or `undefined` if not identifiable.
- `version`: A string representing the browser's version, or `undefined`.
## `device`
An object containing information about the device used in the request.
- `model`: A string representing the model of the device, or `undefined`.
- `type`: A string representing the type of the device, such as `console`, `mobile`, `tablet`, `smarttv`, `wearable`, `embedded`, or `undefined`.
- `vendor`: A string representing the vendor of the device, or `undefined`.
## `engine`
An object containing information about the browser's engine.
- `name`: A string representing the engine's name. Possible values include: `Amaya`, `Blink`, `EdgeHTML`, `Flow`, `Gecko`, `Goanna`, `iCab`, `KHTML`, `Links`, `Lynx`, `NetFront`, `NetSurf`, `Presto`, `Tasman`, `Trident`, `w3m`, `WebKit` or `undefined`.
- `version`: A string representing the engine's version, or `undefined`.
## `os`
An object containing information about the operating system.
- `name`: A string representing the name of the OS, or `undefined`.
- `version`: A string representing the version of the OS, or `undefined`.
## `cpu`
An object containing information about the CPU architecture.
- `architecture`: A string representing the architecture of the CPU. Possible values include: `68k`, `amd64`, `arm`, `arm64`, `armhf`, `avr`, `ia32`, `ia64`, `irix`, `irix64`, `mips`, `mips64`, `pa-risc`, `ppc`, `sparc`, `sparc64` or `undefined`