docs: use relative doc links in instant-navigation error pages (#96672)

These links no longer need to point to preview.
This commit is contained in:
Joseph
2026-08-04 22:30:57 +02:00
committed by GitHub
parent 39b7da2ee8
commit 44c3ec6bb5
16 changed files with 336 additions and 352 deletions
+24 -25
View File
@@ -17,17 +17,16 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a Client Component called a navigation hook ([`usePathname`](https://preview.nextjs.org/docs/app/api-reference/functions/use-pathname), [`useParams`](https://preview.nextjs.org/docs/app/api-reference/functions/use-params), [`useSearchParams`](https://preview.nextjs.org/docs/app/api-reference/functions/use-search-params#prerendering), [`useSelectedLayoutSegment`](https://preview.nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment), or [`useSelectedLayoutSegments`](https://preview.nextjs.org/docs/app/api-reference/functions/use-selected-layout-segments)) outside of a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js prerenders as much of a route as possible before a request arrives. These hooks read URL data that is not available during prerendering, so the component needs a fallback to include in the [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell).
During [prerendering](/docs/app/glossary#prerendering), a Client Component called a navigation hook ([`usePathname`](/docs/app/api-reference/functions/use-pathname), [`useParams`](/docs/app/api-reference/functions/use-params), [`useSearchParams`](/docs/app/api-reference/functions/use-search-params#prerendering), [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment), or [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments)) outside of a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js prerenders as much of a route as possible before a request arrives. These hooks read URL data that is not available during prerendering, so the component needs a fallback to include in the [static shell](/docs/app/glossary#static-shell).
The `useSearchParams` hook triggers this error on any prerendered route because search params come from the request URL. The other four hooks trigger it when the route has dynamic params and is rendered per-request.
Server-side request-bound reads ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers)) have different fixes. See [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime). For unstable values like [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) in Client Components, see [Next.js encountered the unstable value Math.random() in a Client Component](/docs/messages/blocking-prerender-random-client).
Server-side request-bound reads ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers)) have different fixes. See [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime). For unstable values like [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) in Client Components, see [Next.js encountered the unstable value Math.random() in a Client Component](/docs/messages/blocking-prerender-random-client).
## Ways to fix this
@@ -90,7 +89,7 @@ export function Search() {
}
```
Learn more: [`useSearchParams` prerendering behavior](https://preview.nextjs.org/docs/app/api-reference/functions/use-search-params#prerendering)
Learn more: [`useSearchParams` prerendering behavior](/docs/app/api-reference/functions/use-search-params#prerendering)
#### Push the hook read down to the leaf
@@ -150,7 +149,7 @@ The nav links prerender into the static shell with their final `href` and label.
To style the parent `<Link>` itself instead (for example, bolding the active label), have the leaf set `data-active` and read it from the parent with `has-data-active:font-bold`.
Learn more: [`usePathname`](https://preview.nextjs.org/docs/app/api-reference/functions/use-pathname), [Creating an active link component with `useSelectedLayoutSegment`](https://preview.nextjs.org/docs/app/api-reference/functions/use-selected-layout-segment#creating-an-active-link-component)
Learn more: [`usePathname`](/docs/app/api-reference/functions/use-pathname), [Creating an active link component with `useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment#creating-an-active-link-component)
#### Wrap a sibling that uses the hook value
@@ -193,20 +192,20 @@ The sibling renders nothing visible, so an empty fallback is correct. There is n
### Trade-off
The user sees the fallback on the first paint of the suspended region, then it swaps to the real value once the hook resolves after hydration. Choose a fallback shape that matches the final layout so the swap doesn't cause a layout shift. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The user sees the fallback on the first paint of the suspended region, then it swaps to the real value once the hook resolves after hydration. Choose a fallback shape that matches the final layout so the swap doesn't cause a layout shift. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Place the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary as close to the hook call as possible. Wrapping a large subtree forces the entire subtree into the fallback and loses prerendered content.
- The fallback must be synchronous and deterministic. Do not call [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random), [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID), or [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) inside it. Each one raises a separate [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- The fallback must be synchronous and deterministic. Do not call [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random), [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID), or [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) inside it. Each one raises a separate [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- A nav rendered in a layout suspends on any page below it that reads URL data the layout's static shell doesn't know. Push the hook read down to the smallest leaf that needs the value so the rest of the nav stays prerendered.
- The static shell does not include the active state. The fallback paints first, then the indicator hydrates and the active style appears. For active-link patterns where the flash is visible, add an [inline script that runs before paint](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) to set the active attribute from `location.pathname` so the correct link is styled on the first paint.
- [`usePathname`](https://preview.nextjs.org/docs/app/api-reference/functions/use-pathname#avoid-hydration-mismatch-with-rewrites) reads the source path on the server when the request was rewritten in `next.config` or middleware, while the browser sees the rewritten path. The active state on a rewritten route resolves to the wrong link on the server and corrects itself on hydration. If your app uses rewrites, defer the read until after mount as the [docs recommend](https://preview.nextjs.org/docs/app/api-reference/functions/use-pathname#avoid-hydration-mismatch-with-rewrites).
- The static shell does not include the active state. The fallback paints first, then the indicator hydrates and the active style appears. For active-link patterns where the flash is visible, add an [inline script that runs before paint](/docs/app/guides/preventing-flash-before-hydration) to set the active attribute from `location.pathname` so the correct link is styled on the first paint.
- [`usePathname`](/docs/app/api-reference/functions/use-pathname#avoid-hydration-mismatch-with-rewrites) reads the source path on the server when the request was rewritten in `next.config` or middleware, while the browser sees the rewritten path. The active state on a rewritten route resolves to the wrong link on the server and corrects itself on hydration. If your app uses rewrites, defer the read until after mount as the [docs recommend](/docs/app/api-reference/functions/use-pathname#avoid-hydration-mismatch-with-rewrites).
## Allow blocking route
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
### Patterns
@@ -222,11 +221,11 @@ export default function Page() {
}
```
Learn more: [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation).
Learn more: [Ensuring instant navigations](/docs/app/guides/instant-navigation).
#### Opt the layout out
When the shared layout itself can't ship instantly (it reads URL data of its own that has no meaningful fallback), set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
When the shared layout itself can't ship instantly (it reads URL data of its own that has no meaningful fallback), set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
```jsx filename="app/dashboard/layout.js"
export const instant = false
@@ -236,11 +235,11 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use either pattern when:
- The route needs request-time data high in the tree to decide what to render, so there is no meaningful [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) worth showing first.
- The route needs request-time data high in the tree to decide what to render, so there is no meaningful [static shell](/docs/app/glossary#static-shell) worth showing first.
- You're migrating a route incrementally and want to defer the lifetime decision without changing how the page renders today.
For a client-hook error this is rarely the right answer. The hook reads a small piece of URL data, and a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary around that read keeps the rest of the route prerendered. Choose [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) when feasible.
@@ -251,23 +250,23 @@ Navigations to this route are not instant. The user waits for the full server re
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+11 -12
View File
@@ -17,10 +17,9 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
A [Client Component](/docs/app/getting-started/server-and-client-components#using-client-components) called a synchronous [Web Crypto](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) API that produces a random value ([`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID), [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues)) inline during render, and the surrounding tree had no [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. Client Components are server-side rendered on first load, so Next.js can't bake an unpredictable value into the prerendered HTML. The SSR value won't match the value the client computes on hydration, so you need to choose: defer the value behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so SSR can stream it, or move the call into [`useEffect`](https://react.dev/reference/react/useEffect) (or an event handler) so it only runs on the client.
@@ -85,15 +84,15 @@ export function CorrelationId() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
### Trade-off
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling a crypto API in any of them raises this same error. Use stable placeholder content.
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling a crypto API in any of them raises this same error. Use stable placeholder content.
- The inner Client Component still runs during SSR, behind the boundary. If you need to guarantee the value only runs in the browser, use [Move into effect or event handler](#move-into-effect-or-event-handler) instead.
- A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary only fixes the prerender/hydration mismatch, not client re-renders. If the component using the crypto API re-renders on the client (a parent state change, a context update), it produces a new value each time. To stabilize the value across re-renders, call the crypto API once in a [`useState`](https://react.dev/reference/react/useState) initializer or [`useRef`](https://react.dev/reference/react/useRef), or compute it on the server and pass it down as a prop.
@@ -188,7 +187,7 @@ Learn more: [`useRef`](https://react.dev/reference/react/useRef).
### Trade-off
The user sees the placeholder briefly before the real value. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The user sees the placeholder briefly before the real value. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -201,7 +200,7 @@ The user sees the placeholder briefly before the real value. For interactions th
### Suspend with `use(io())`
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
```jsx filename="app/components/request-id.js"
'use client'
@@ -229,17 +228,17 @@ export default function Page() {
}
```
Learn more: [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io).
Learn more: [`io`](/docs/app/api-reference/functions/io).
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `crypto.randomUUID()` and related APIs return a different value on every call, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `crypto.randomUUID()` and related APIs return a different value on every call, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
+21 -22
View File
@@ -17,13 +17,12 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a Server Component called a synchronous [Web Crypto](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) or Node [`crypto`](https://nodejs.org/api/crypto.html) API that produces a random value ([`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID), [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues), [`crypto.randomBytes()`](https://nodejs.org/api/crypto.html#cryptorandombytessize-callback), [`crypto.generateKeyPairSync()`](https://nodejs.org/api/crypto.html#cryptogeneratekeypairsynctype-options)) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake an unpredictable value into the prerendered HTML. The value at build time will differ from the value at runtime, so you need to choose: cache the generated value so it's stable, defer the call behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
During [prerendering](/docs/app/glossary#prerendering), a Server Component called a synchronous [Web Crypto](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) or Node [`crypto`](https://nodejs.org/api/crypto.html) API that produces a random value ([`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID), [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues), [`crypto.randomBytes()`](https://nodejs.org/api/crypto.html#cryptorandombytessize-callback), [`crypto.generateKeyPairSync()`](https://nodejs.org/api/crypto.html#cryptogeneratekeypairsynctype-options)) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake an unpredictable value into the prerendered HTML. The value at build time will differ from the value at runtime, so you need to choose: cache the generated value so it's stable, defer the call behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
Other unpredictable APIs ([`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random), [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now)) have parallel error pages: see [`Math.random()`](/docs/messages/blocking-prerender-random) and [`Date.now()`](/docs/messages/blocking-prerender-current-time). The Client Component case is handled at [Crypto APIs in a Client Component](/docs/messages/blocking-prerender-crypto-client).
@@ -64,13 +63,13 @@ Other unpredictable APIs ([`Math.random()`](https://developer.mozilla.org/en-US/
## Generate on every request
Choose this fix when each request needs a fresh token: a session ID, an OAuth state, a single-use nonce, a CSRF token. Add [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
Choose this fix when each request needs a fresh token: a session ID, an OAuth state, a single-use nonce, a CSRF token. Add [`await connection()`](/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
### Patterns
#### Use `await connection()` before the crypto call
Call [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before the crypto API. Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Call [`connection()`](/docs/app/api-reference/functions/connection) before the crypto API. Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Push the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary as close to the crypto call as possible. If the parent has cached content, isolate the crypto read in its own component so only that piece falls behind the boundary.
@@ -100,7 +99,7 @@ export async function CsrfToken() {
#### Alternative: `await io()`
Use [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
Use [`io()`](/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
```jsx filename="app/dashboard/csrf-token.js"
import { io } from 'next/cache'
@@ -111,11 +110,11 @@ export async function CsrfToken() {
}
```
Learn more: [`connection`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io), [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [`connection`](/docs/app/api-reference/functions/connection), [`io`](/docs/app/api-reference/functions/io), [Streaming](/docs/app/guides/streaming).
#### Switch to an async crypto API
When an async equivalent of the API exists, prefer it. Async crypto operations integrate with [`<Suspense>`](https://react.dev/reference/react/Suspense) naturally and don't need [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection): the [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) already tells Next.js the surrounding scope is request-time.
When an async equivalent of the API exists, prefer it. Async crypto operations integrate with [`<Suspense>`](https://react.dev/reference/react/Suspense) naturally and don't need [`await connection()`](/docs/app/api-reference/functions/connection): the [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) already tells Next.js the surrounding scope is request-time.
```jsx filename="app/page.js"
import { randomBytes } from 'node:crypto'
@@ -142,11 +141,11 @@ Learn more: [Node `crypto` API](https://nodejs.org/docs/latest/api/crypto.html).
### Trade-off
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it paints. When the fallback is visible UI, design it to approximate the final layout so it doesn't cause a layout shift when the content arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it paints. When the fallback is visible UI, design it to approximate the final layout so it doesn't cause a layout shift when the content arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling a crypto API in any of them raises this same error.
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling a crypto API in any of them raises this same error.
- For genuinely security-critical tokens (session IDs, CSRF), [Generate on every request](#generate-on-every-request) is the only correct choice. Caching a CSRF token across visitors defeats its purpose.
- This error only fires for synchronous random-producing APIs. Async crypto operations ([`crypto.subtle.digest()`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest), [`crypto.generateKeyPair()`](https://nodejs.org/api/crypto.html#cryptogeneratekeypairtype-options-callback)) integrate with [`<Suspense>`](https://react.dev/reference/react/Suspense) naturally and don't trip the error.
@@ -158,7 +157,7 @@ Choose this fix when the generated value is a _key into another cached operation
#### Cache the token alongside the query that uses it
Wrap both the token generation and the call that consumes it in the same [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) function.
Wrap both the token generation and the call that consumes it in the same [`use cache`](/docs/app/api-reference/directives/use-cache) function.
```jsx filename="app/page.js"
async function getCachedData() {
@@ -173,11 +172,11 @@ export default async function Page() {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache).
#### Tag the cache for explicit rotation
When you want to rotate the token on a schedule or in response to an event, tag the entry with [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag). Invalidate from a Server Action with [`updateTag`](/docs/app/api-reference/functions/updateTag) (read-your-own-writes: the next request waits for fresh data) or from a Route Handler with [`revalidateTag`](https://preview.nextjs.org/docs/app/api-reference/functions/revalidateTag) (stale-while-revalidate).
When you want to rotate the token on a schedule or in response to an event, tag the entry with [`cacheTag`](/docs/app/api-reference/functions/cacheTag). Invalidate from a Server Action with [`updateTag`](/docs/app/api-reference/functions/updateTag) (read-your-own-writes: the next request waits for fresh data) or from a Route Handler with [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) (stale-while-revalidate).
```jsx filename="app/page.js"
import { cacheTag } from 'next/cache'
@@ -189,7 +188,7 @@ async function getApiToken() {
}
```
Learn more: [How revalidation works](https://preview.nextjs.org/docs/app/guides/how-revalidation-works).
Learn more: [How revalidation works](/docs/app/guides/how-revalidation-works).
### Trade-off
@@ -198,12 +197,12 @@ Every visitor in the cache window uses the same generated value. That's the righ
### Gotchas
- Don't cache a value that's intended as a security token for visitors. If the same "random" UUID is used as a CSRF token for every user, the protection is gone.
- [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) can't combine with [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) in the same scope, so you can't key the cached value by user identity from inside the cached function.
- If you cache a function and still see this error, the [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
- [`use cache`](/docs/app/api-reference/directives/use-cache) can't combine with [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) in the same scope, so you can't key the cached value by user identity from inside the cached function.
- If you cache a function and still see this error, the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
### Short-lived caches
[`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](https://preview.nextjs.org/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
[`use cache`](/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
To keep the page prerendered, use a profile with a longer revalidate window such as `"default"` (15 minutes), `"hours"`, or `"days"`. If a short profile is intentional, treat the value as dynamic and use [Generate on every request](#generate-on-every-request) instead.
@@ -240,7 +239,7 @@ Learn more: [Client Components](/docs/app/getting-started/server-and-client-comp
### Trade-off
The first paint shows the SSR fallback (often `null`), and the value appears only after the browser hydrates the component. That's fine for client-only state but wrong for tokens that have to be in the prerendered HTML. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The first paint shows the SSR fallback (often `null`), and the value appears only after the browser hydrates the component. That's fine for client-only state but wrong for tokens that have to be in the prerendered HTML. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -251,11 +250,11 @@ The first paint shows the SSR fallback (often `null`), and the value appears onl
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `crypto.randomUUID()` and related APIs return a different value on every call, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `crypto.randomUUID()` and related APIs return a different value on every call, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
@@ -17,10 +17,9 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
A [Client Component](/docs/app/getting-started/server-and-client-components#using-client-components) called [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date), or [`new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) inline during render, and the surrounding tree had no [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. Client Components are server-side rendered on first load, so Next.js can't bake "now" into the prerendered HTML. The SSR timestamp won't match the value the client computes on hydration, so you need to choose: defer the value behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so SSR can stream it, or move the call into [`useEffect`](https://react.dev/reference/react/useEffect) (or an event handler) so it only runs on the client.
@@ -98,15 +97,15 @@ export function RelativeTime({ timestamp }) {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
### Trade-off
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error. Use stable placeholder content.
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error. Use stable placeholder content.
- The inner Client Component still runs during SSR, behind the boundary. If you need to guarantee the timestamp only runs in the browser, use [Move into effect or event handler](#move-into-effect-or-event-handler) instead.
- A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary only fixes the prerender/hydration mismatch, not client re-renders. If the component using [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) re-renders on the client (a parent state change, a context update), it reads a fresh timestamp each time. To stabilize the value across re-renders, capture [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) once in a [`useState`](https://react.dev/reference/react/useState) initializer or [`useRef`](https://react.dev/reference/react/useRef), or compute it on the server and pass it down as a prop.
@@ -168,7 +167,7 @@ export function Snapshot() {
### Trade-off
The user sees the placeholder briefly before the real timestamp. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The user sees the placeholder briefly before the real timestamp. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -218,7 +217,7 @@ Learn more: [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/A
### Cache the value in a Server Component
When the timestamp doesn't need to reflect the user's current visit and lives inside a Client Component only because of where it's rendered, lift the read into a Server Component above with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). The canonical case is a copyright year in a footer.
When the timestamp doesn't need to reflect the user's current visit and lives inside a Client Component only because of where it's rendered, lift the read into a Server Component above with [`use cache`](/docs/app/api-reference/directives/use-cache). The canonical case is a copyright year in a footer.
```jsx filename="app/layout.js"
import { cacheLife } from 'next/cache'
@@ -243,7 +242,7 @@ Learn more: [`Date.now()` during prerendering](/docs/messages/blocking-prerender
### Suspend with `use(io())`
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
```jsx filename="app/components/last-updated.js"
'use client'
@@ -271,17 +270,17 @@ export default function Page() {
}
```
Learn more: [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io).
Learn more: [`io`](/docs/app/api-reference/functions/io).
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `new Date()` and `Date.now()` return a different value on every render, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `new Date()` and `Date.now()` return a different value on every render, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
+20 -21
View File
@@ -17,13 +17,12 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a Server Component called [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date), or [`new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake "now" into the prerendered HTML. The timestamp at build time will be stale at runtime, so you need to choose: cache the value (treat it as "now-ish" with a tolerated drift), defer the read behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
During [prerendering](/docs/app/glossary#prerendering), a Server Component called [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date), or [`new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake "now" into the prerendered HTML. The timestamp at build time will be stale at runtime, so you need to choose: cache the value (treat it as "now-ish" with a tolerated drift), defer the read behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
Other unpredictable APIs ([`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random), [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID)) have parallel error pages: see [`Math.random()`](/docs/messages/blocking-prerender-random) and [crypto APIs](/docs/messages/blocking-prerender-crypto). The Client Component case is handled at [`Date.now()` in a Client Component](/docs/messages/blocking-prerender-current-time-client).
@@ -74,13 +73,13 @@ Other unpredictable APIs ([`Math.random()`](https://developer.mozilla.org/en-US/
## Generate on every request
Choose this fix when the user needs to see the current time. A "last updated at" banner, a server-issued timestamp on a transaction, a "happy new year" banner that flips at midnight. Add [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
Choose this fix when the user needs to see the current time. A "last updated at" banner, a server-issued timestamp on a transaction, a "happy new year" banner that flips at midnight. Add [`await connection()`](/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
### Patterns
#### Use `await connection()` before the timestamp read
Call [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before the [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) or [`new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) call. Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Call [`connection()`](/docs/app/api-reference/functions/connection) before the [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) or [`new Date()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) call. Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Push the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary as close to the timestamp read as possible. If the parent has cached content (metrics, headers, navigation), isolate the timestamp in its own component so only that piece falls behind the boundary.
@@ -110,7 +109,7 @@ export async function UpdatedAt() {
#### Alternative: `await io()`
Use [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
Use [`io()`](/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
```jsx filename="app/dashboard/updated-at.js"
import { io } from 'next/cache'
@@ -121,26 +120,26 @@ export async function UpdatedAt() {
}
```
Learn more: [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io), [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [`io`](/docs/app/api-reference/functions/io), [Streaming](/docs/app/guides/streaming).
### Trade-off
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it paints. Design the fallback so it approximates the final layout. A generic spinner or empty box causes a layout shift when the content arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it paints. Design the fallback so it approximates the final layout. A generic spinner or empty box causes a layout shift when the content arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error.
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error.
- If you're showing a relative time ("3 minutes ago"), the relative formatting belongs on the client so it can update without a re-render. See [Render on the client](#render-on-the-client).
## Cache the timestamp
Choose this fix when a stale timestamp is acceptable for the cache window. A "last refreshed" footer on a daily report, an "as of" banner on an hourly chart. Move the [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) call into a function with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). Next.js evaluates the function once per cache key and reuses the result.
Choose this fix when a stale timestamp is acceptable for the cache window. A "last refreshed" footer on a daily report, an "as of" banner on an hourly chart. Move the [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) call into a function with [`use cache`](/docs/app/api-reference/directives/use-cache). Next.js evaluates the function once per cache key and reuses the result.
### Patterns
#### Cache the producer function
Wrap the timestamp read in a function with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). The returned value is part of the cache entry, so every consumer sees the same timestamp until the cache is invalidated.
Wrap the timestamp read in a function with [`use cache`](/docs/app/api-reference/directives/use-cache). The returned value is part of the cache entry, so every consumer sees the same timestamp until the cache is invalidated.
```jsx filename="app/page.js"
async function getRenderedAt() {
@@ -154,7 +153,7 @@ export default async function Page() {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache).
#### Cache the timestamp alongside the data it relates to
@@ -198,7 +197,7 @@ export default async function Layout({ children }) {
#### Set the rotation window with `cacheLife`
When you want the timestamp to refresh on a schedule, set a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile.
When you want the timestamp to refresh on a schedule, set a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile.
```jsx filename="app/page.js"
import { cacheLife } from 'next/cache'
@@ -210,7 +209,7 @@ async function getHourlyTimestamp() {
}
```
Learn more: [How to configure cache lifetimes](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife).
Learn more: [How to configure cache lifetimes](/docs/app/api-reference/functions/cacheLife).
### Trade-off
@@ -219,11 +218,11 @@ Every visitor in the cache window sees the same timestamp. That's fine for "as o
### Gotchas
- A cached timestamp is the time of the most recent cache miss, not the time of the current visit. If users expect the displayed time to match their visit, use [Generate on every request](#generate-on-every-request) instead.
- If you cache a function and still see this error, the [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
- If you cache a function and still see this error, the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
### Short-lived caches
[`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](https://preview.nextjs.org/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
[`use cache`](/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
To keep the page prerendered, use a profile with a longer revalidate window such as `"default"` (15 minutes), `"hours"`, or `"days"`. If a short profile is intentional, treat the value as dynamic and use [Generate on every request](#generate-on-every-request) instead.
@@ -266,7 +265,7 @@ Learn more: [Client Components](/docs/app/getting-started/server-and-client-comp
### Trade-off
The first paint shows the SSR fallback (often `null` or an em-dash), and the timestamp appears only after the browser hydrates the component. That's fine for time-of-day displays but wrong for timestamps that have to be in the prerendered HTML. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The first paint shows the SSR fallback (often `null` or an em-dash), and the timestamp appears only after the browser hydrates the component. That's fine for time-of-day displays but wrong for timestamps that have to be in the prerendered HTML. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -308,11 +307,11 @@ Learn more: [`performance.now()`](https://developer.mozilla.org/en-US/docs/Web/A
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `new Date()` and `Date.now()` return a different value on every render, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `new Date()` and `Date.now()` return a different value on every render, so the prerender can't bake them into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
+44 -45
View File
@@ -17,17 +17,16 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) request, database call, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), or other asynchronous IO ran outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't prerender the part of the tree that depends on this data, so navigations to this route block instead of being [instant](https://preview.nextjs.org/docs/app/guides/instant-navigation).
During [prerendering](/docs/app/glossary#prerendering), a [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) request, database call, [`await connection()`](/docs/app/api-reference/functions/connection), or other asynchronous IO ran outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't prerender the part of the tree that depends on this data, so navigations to this route block instead of being [instant](/docs/app/guides/instant-navigation).
Request-bound reads ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)) have different fixes. See [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
Request-bound reads ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)) have different fixes. See [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
This error can also appear during a client-side navigation when the data access sits inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary from a parent layout but that boundary is too high. It wraps the entire segment instead of only the dynamic part, so the navigation still blocks. Push the boundary closer to the data access so the rest of the segment stays in the [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell). See [Choosing where to place the boundary](#choosing-where-to-place-the-boundary).
This error can also appear during a client-side navigation when the data access sits inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary from a parent layout but that boundary is too high. It wraps the entire segment instead of only the dynamic part, so the navigation still blocks. Push the boundary closer to the data access so the rest of the segment stays in the [static shell](/docs/app/glossary#static-shell). See [Choosing where to place the boundary](#choosing-where-to-place-the-boundary).
## Ways to fix this
@@ -65,7 +64,7 @@ This error can also appear during a client-side navigation when the data access
## Wrap in or move into Suspense
Choose this fix when the data must be fresh on every request. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary lets the static shell ship instantly while the dynamic region [streams](https://preview.nextjs.org/docs/app/glossary#streaming) in once the data resolves.
Choose this fix when the data must be fresh on every request. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary lets the static shell ship instantly while the dynamic region [streams](/docs/app/glossary#streaming) in once the data resolves.
### Patterns
@@ -87,7 +86,7 @@ export default function Page() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
#### Push the data access down to the leaf
@@ -115,7 +114,7 @@ export async function LatestTransactions() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
#### Add a boundary per leaf so they stream in parallel
@@ -138,11 +137,11 @@ export default function Page() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
#### Use `loading.js` for the whole segment
When the entire page depends on the failing data access and there's nothing static to render above it, a [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading) file in the segment is the shorthand. Next.js wraps `{children}` of the layout in `<Suspense>` automatically.
When the entire page depends on the failing data access and there's nothing static to render above it, a [`loading.js`](/docs/app/api-reference/file-conventions/loading) file in the segment is the shorthand. Next.js wraps `{children}` of the layout in `<Suspense>` automatically.
```jsx filename="app/dashboard/loading.js"
export default function Loading() {
@@ -152,11 +151,11 @@ export default function Loading() {
> **Good to know**: A `loading.js` file wraps the segment's `{children}` in one Suspense boundary. Parent layouts above it still prerender, but everything inside the segment sits behind the fallback. If page-level content could be prerendered (a static intro, a known title), use explicit `<Suspense>` boundaries inside `page.js` around only the dynamic parts.
Learn more: [`loading.js` and instant loading states](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading).
Learn more: [`loading.js` and instant loading states](/docs/app/api-reference/file-conventions/loading).
### Trade-off
The shell ships immediately, but the user sees a loading state for the streamed region on every request. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The shell ships immediately, but the user sees a loading state for the streamed region on every request. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Choosing where to place the boundary
@@ -165,27 +164,27 @@ The location of the boundary controls what the user sees during the navigation:
- A high boundary (around the whole page) gives one loading state for everything. Less work to set up, but the user loses context about where they were going.
- A low boundary (around the specific component that fetches) keeps surrounding content visible and only shows a fallback for the part that is in flight. Preferred when the surrounding shell has cached content.
A useful rule: **push the boundary as low as possible** while keeping the fallback meaningful. The cached content above the boundary becomes part of the [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) on navigation. Wrapping individual pieces or wrapping the whole page in one boundary stream the same way, but a lower boundary keeps more prerendered content visible during the navigation. See [Maximizing the static shell](https://preview.nextjs.org/docs/app/getting-started/caching#streaming-uncached-data) for the canonical pattern.
A useful rule: **push the boundary as low as possible** while keeping the fallback meaningful. The cached content above the boundary becomes part of the [static shell](/docs/app/glossary#static-shell) on navigation. Wrapping individual pieces or wrapping the whole page in one boundary stream the same way, but a lower boundary keeps more prerendered content visible during the navigation. See [Maximizing the static shell](/docs/app/getting-started/caching#streaming-uncached-data) for the canonical pattern.
### Gotchas
- The fallback must be deterministic. Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) inside the fallback raises a separate [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or an uncached [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- The fallback must be deterministic. Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) inside the fallback raises a separate [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](/docs/app/api-reference/functions/cookies) or an uncached [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- If the failing route is `/_not-found` and you don't have a `not-found.tsx` file, the read is in the root layout. `/_not-found` is a real prerendered route that inherits the root layout, so an uncached read there fails on the synthetic route too. Run `next build --debug-prerender` to confirm the originating file, and fix it at the layout, not by adding a `not-found.tsx`.
- Boundary placement affects client navigations between sibling routes differently than initial page loads. Validation surfaces this in the dev server and at build time. See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
- Root-element attributes (`<html lang>`, `<html dir>`, `<html data-theme>`) can't be wrapped in `<Suspense>`. You can't suspend the document root, and a boundary inside `<html>` still leaves the attribute itself server-cookie-dependent. Move the read to a pre-paint client script per [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) and add `suppressHydrationWarning` on `<html>` so React doesn't flag the script's mutation as a mismatch.
- Boundary placement affects client navigations between sibling routes differently than initial page loads. Validation surfaces this in the dev server and at build time. See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
- Root-element attributes (`<html lang>`, `<html dir>`, `<html data-theme>`) can't be wrapped in `<Suspense>`. You can't suspend the document root, and a boundary inside `<html>` still leaves the attribute itself server-cookie-dependent. Move the read to a pre-paint client script per [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) and add `suppressHydrationWarning` on `<html>` so React doesn't flag the script's mutation as a mismatch.
## Cache the component or data
Choose this fix when the data does not need to be regenerated on every request. Move the call into a function and add the [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) directive as the first statement of the function body. The function still runs the underlying query, but Next.js caches the result for the configured lifetime and the surrounding route becomes prerenderable.
Choose this fix when the data does not need to be regenerated on every request. Move the call into a function and add the [`use cache`](/docs/app/api-reference/directives/use-cache) directive as the first statement of the function body. The function still runs the underlying query, but Next.js caches the result for the configured lifetime and the surrounding route becomes prerenderable.
This fix does not apply to [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection). The whole point of `connection()` is to opt into per-request rendering for the wrapped subtree, so caching it would defeat the purpose. Use [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) instead.
This fix does not apply to [`connection()`](/docs/app/api-reference/functions/connection). The whole point of `connection()` is to opt into per-request rendering for the wrapped subtree, so caching it would defeat the purpose. Use [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) instead.
### Patterns
#### Cache the data-access function
Move the [`fetch()`](https://preview.nextjs.org/docs/app/getting-started/fetching-data) or database call into its own function and mark that function with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). Arguments to the function and closed-over variables become part of the cache key, so prefer passing the values you depend on as arguments to make the contract explicit.
Move the [`fetch()`](/docs/app/getting-started/fetching-data) or database call into its own function and mark that function with [`use cache`](/docs/app/api-reference/directives/use-cache). Arguments to the function and closed-over variables become part of the cache key, so prefer passing the values you depend on as arguments to make the contract explicit.
```jsx filename="app/dashboard/page.js"
async function getRecentTransactions(limit) {
@@ -202,11 +201,11 @@ export default async function Page() {
}
```
Learn more: [Fetching Data](https://preview.nextjs.org/docs/app/getting-started/fetching-data).
Learn more: [Fetching Data](/docs/app/getting-started/fetching-data).
#### Cache the whole component
When the component does nothing but read data and render it, mark the [component itself](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache#caching-a-components-output-with-use-cache) with `use cache`. Next.js caches the rendered JSX, which is cheaper to reuse than recomputing it from the cached data.
When the component does nothing but read data and render it, mark the [component itself](/docs/app/api-reference/directives/use-cache#caching-a-components-output-with-use-cache) with `use cache`. Next.js caches the rendered JSX, which is cheaper to reuse than recomputing it from the cached data.
```jsx filename="app/dashboard/transaction-list.js"
export async function TransactionList({ limit }) {
@@ -222,11 +221,11 @@ export async function TransactionList({ limit }) {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache#caching-a-components-output-with-use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache#caching-a-components-output-with-use-cache).
#### Tag the cache for targeted invalidation
Choose this when you want control over when the cached value is refreshed. Tag the entry with [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag) and invalidate it on demand: call [`updateTag`](/docs/app/api-reference/functions/updateTag) from a [Server Action](https://preview.nextjs.org/docs/app/getting-started/mutating-data) when the user performed the mutation and should see fresh data on the next request, or [`revalidateTag`](https://preview.nextjs.org/docs/app/api-reference/functions/revalidateTag) from a route handler, cron, admin tool, or incoming webhook for stale-while-revalidate refreshes. Tags add an on-demand invalidation path on top of the [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) expiration window. The two are independent.
Choose this when you want control over when the cached value is refreshed. Tag the entry with [`cacheTag`](/docs/app/api-reference/functions/cacheTag) and invalidate it on demand: call [`updateTag`](/docs/app/api-reference/functions/updateTag) from a [Server Action](/docs/app/getting-started/mutating-data) when the user performed the mutation and should see fresh data on the next request, or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) from a route handler, cron, admin tool, or incoming webhook for stale-while-revalidate refreshes. Tags add an on-demand invalidation path on top of the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) expiration window. The two are independent.
```jsx filename="app/dashboard/page.js"
import { cacheTag } from 'next/cache'
@@ -238,11 +237,11 @@ async function getRecentTransactions() {
}
```
Learn more: [How revalidation works](https://preview.nextjs.org/docs/app/guides/how-revalidation-works).
Learn more: [How revalidation works](/docs/app/guides/how-revalidation-works).
#### Set an explicit `cacheLife` profile
When the data has a natural shelf-life (hourly metrics, daily aggregates), pick a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile that matches. Without a profile, Next.js uses the project default.
When the data has a natural shelf-life (hourly metrics, daily aggregates), pick a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile that matches. Without a profile, Next.js uses the project default.
```jsx filename="app/dashboard/page.js"
import { cacheLife } from 'next/cache'
@@ -254,28 +253,28 @@ async function getDashboard() {
}
```
Learn more: [How to configure cache lifetimes](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife).
Learn more: [How to configure cache lifetimes](/docs/app/api-reference/functions/cacheLife).
### Trade-off
Freshness becomes a property of the cache configuration, not the data source. The cached response is reused until [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) revalidates or expires, or until [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag) is invalidated. Plan invalidations alongside the code that mutates the data. Call [`updateTag`](/docs/app/api-reference/functions/updateTag) from a [Server Action](https://preview.nextjs.org/docs/app/getting-started/mutating-data) when the user performed the mutation and should see fresh data on the next request, or [`revalidateTag`](https://preview.nextjs.org/docs/app/api-reference/functions/revalidateTag) from a route handler, cron, or webhook for stale-while-revalidate refreshes.
Freshness becomes a property of the cache configuration, not the data source. The cached response is reused until [`cacheLife`](/docs/app/api-reference/functions/cacheLife) revalidates or expires, or until [`cacheTag`](/docs/app/api-reference/functions/cacheTag) is invalidated. Plan invalidations alongside the code that mutates the data. Call [`updateTag`](/docs/app/api-reference/functions/updateTag) from a [Server Action](/docs/app/getting-started/mutating-data) when the user performed the mutation and should see fresh data on the next request, or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) from a route handler, cron, or webhook for stale-while-revalidate refreshes.
### Gotchas
- Variables captured from the surrounding scope are automatically bound as part of the cache key. That keeps cached entries per-value, but it also means a wide closure can balloon the key surface. Prefer passing the dependencies you care about as function arguments so the contract is explicit.
- The `"use cache"` directive runs on the server. It can't wrap a function that uses runtime APIs such as [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers). Read those outside the cached scope and pass the values as arguments, or use [`"use cache: private"`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache-private).
- The `"use cache"` directive runs on the server. It can't wrap a function that uses runtime APIs such as [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers). Read those outside the cached scope and pass the values as arguments, or use [`"use cache: private"`](/docs/app/api-reference/directives/use-cache-private).
- If you cache a function and still see this error, the `cacheLife` may be too short to prerender. See [Short-lived caches](#short-lived-caches).
- The default in-memory cache is per-server-instance. If the upstream call is expensive and you want a shared cache across instances, use [`"use cache: remote"`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache-remote) instead. It trades a network roundtrip for a single cache shared by all servers.
- The default in-memory cache is per-server-instance. If the upstream call is expensive and you want a shared cache across instances, use [`"use cache: remote"`](/docs/app/api-reference/directives/use-cache-remote) instead. It trades a network roundtrip for a single cache shared by all servers.
### Short-lived caches
[`"use cache"`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](https://preview.nextjs.org/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
[`"use cache"`](/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
To keep the page prerendered, use a profile with a longer revalidate window such as `"default"` (15 minutes), `"hours"`, or `"days"`. If a short profile is intentional, treat the value as dynamic and use [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) instead.
## Allow blocking route
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
### Patterns
@@ -292,11 +291,11 @@ export default async function Page() {
}
```
Learn more: [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation).
Learn more: [Ensuring instant navigations](/docs/app/guides/instant-navigation).
#### Opt the layout out
When the shared layout itself can't ship instantly (it reads runtime data or uncached data of its own), set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
When the shared layout itself can't ship instantly (it reads runtime data or uncached data of its own), set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
```jsx filename="app/dashboard/layout.js"
export const instant = false
@@ -306,11 +305,11 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use either pattern when:
- The route needs request-time data high in the tree to decide what to render (for example auth, tenant, or other gating in a layout), so there is no meaningful [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) worth showing first.
- The route needs request-time data high in the tree to decide what to render (for example auth, tenant, or other gating in a layout), so there is no meaningful [static shell](/docs/app/glossary#static-shell) worth showing first.
- You're migrating a route incrementally and want to defer the lifetime decision without changing how the page renders today.
Don't use this to dismiss the error. Choose [Cache the component or data](#cache-the-component-or-data) or [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) when either is feasible.
@@ -321,23 +320,23 @@ Navigations to this route are not instant. The user waits for the full server re
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+22 -23
View File
@@ -17,15 +17,14 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), [`generateMetadata()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata) performed an uncached data access ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database call, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection)). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js expects metadata to be prerenderable when the rest of the route is. This route's metadata is blocked, but the rest of its content can be prerendered.
During [prerendering](/docs/app/glossary#prerendering), [`generateMetadata()`](/docs/app/api-reference/functions/generate-metadata) performed an uncached data access ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database call, [`await connection()`](/docs/app/api-reference/functions/connection)). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js expects metadata to be prerenderable when the rest of the route is. This route's metadata is blocked, but the rest of its content can be prerendered.
Request-bound reads ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)) in `generateMetadata()` have different fixes. See [Next.js encountered runtime data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-runtime).
Request-bound reads ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)) in `generateMetadata()` have different fixes. See [Next.js encountered runtime data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-runtime).
The viewport equivalent is handled at [Uncached data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-dynamic).
@@ -57,9 +56,9 @@ For errors in the page body rather than metadata, see [Next.js encountered uncac
## Cache the metadata
Choose this fix when the metadata comes from an external source (CMS, database) but doesn't need to change on every request. Add the [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) directive as the first statement inside [`generateMetadata()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata). Next.js caches the returned metadata object and includes it in the prerender.
Choose this fix when the metadata comes from an external source (CMS, database) but doesn't need to change on every request. Add the [`use cache`](/docs/app/api-reference/directives/use-cache) directive as the first statement inside [`generateMetadata()`](/docs/app/api-reference/functions/generate-metadata). Next.js caches the returned metadata object and includes it in the prerender.
This fix does not apply to [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection). The point of `connection()` is to opt into per-request rendering, so caching it would defeat the purpose. Use [Mark the route as dynamic](#mark-the-route-as-dynamic) instead.
This fix does not apply to [`connection()`](/docs/app/api-reference/functions/connection). The point of `connection()` is to opt into per-request rendering, so caching it would defeat the purpose. Use [Mark the route as dynamic](#mark-the-route-as-dynamic) instead.
### Patterns
@@ -90,11 +89,11 @@ export default async function Page({ params }) {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache).
#### Tag the metadata for invalidation
When you publish new content and want the metadata to refresh, tag the entry with [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag). Invalidate from a Server Action with [`updateTag`](/docs/app/api-reference/functions/updateTag) (read-your-own-writes: the next request waits for fresh data) or from a Route Handler with [`revalidateTag`](https://preview.nextjs.org/docs/app/api-reference/functions/revalidateTag).
When you publish new content and want the metadata to refresh, tag the entry with [`cacheTag`](/docs/app/api-reference/functions/cacheTag). Invalidate from a Server Action with [`updateTag`](/docs/app/api-reference/functions/updateTag) (read-your-own-writes: the next request waits for fresh data) or from a Route Handler with [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag).
```jsx filename="app/blog/[slug]/page.js"
import { cacheTag } from 'next/cache'
@@ -109,21 +108,21 @@ export async function generateMetadata({ params }) {
}
```
Learn more: [How revalidation works](https://preview.nextjs.org/docs/app/guides/how-revalidation-works).
Learn more: [How revalidation works](/docs/app/guides/how-revalidation-works).
### Trade-off
Freshness depends on the cache configuration. The metadata stays the same until [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) revalidates or expires, or until [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag) is invalidated. Plan invalidations alongside the code that mutates the content.
Freshness depends on the cache configuration. The metadata stays the same until [`cacheLife`](/docs/app/api-reference/functions/cacheLife) revalidates or expires, or until [`cacheTag`](/docs/app/api-reference/functions/cacheTag) is invalidated. Plan invalidations alongside the code that mutates the content.
### Gotchas
- [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) can't be combined with [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) in the same scope. Inside a cached function, you can't call request-bound APIs. If the metadata needs a request-bound value (a session token to call a protected API), read it outside the cached scope and pass it as an argument, or use [Mark the route as dynamic](#mark-the-route-as-dynamic) instead.
- [`use cache`](/docs/app/api-reference/directives/use-cache) can't be combined with [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) in the same scope. Inside a cached function, you can't call request-bound APIs. If the metadata needs a request-bound value (a session token to call a protected API), read it outside the cached scope and pass it as an argument, or use [Mark the route as dynamic](#mark-the-route-as-dynamic) instead.
- If the metadata function reads `params`, the params become part of the cache key automatically. Each unique param set gets its own cached metadata entry.
- A short [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) (a profile whose `revalidate` is shorter than the prerender's effective lifetime) prevents the metadata from being included in the prerender. The route becomes partially dynamic. Use a longer profile if you want the metadata included in the static shell.
- A short [`cacheLife`](/docs/app/api-reference/functions/cacheLife) (a profile whose `revalidate` is shorter than the prerender's effective lifetime) prevents the metadata from being included in the prerender. The route becomes partially dynamic. Use a longer profile if you want the metadata included in the static shell.
## Mark the route as dynamic
Choose this fix when the rest of the page is fully static and you want the metadata to remain dynamic. Add a small component that calls [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), render `null` from it, and wrap it in [`<Suspense>`](https://react.dev/reference/react/Suspense).
Choose this fix when the rest of the page is fully static and you want the metadata to remain dynamic. Add a small component that calls [`await connection()`](/docs/app/api-reference/functions/connection), render `null` from it, and wrap it in [`<Suspense>`](https://react.dev/reference/react/Suspense).
This error fires specifically because the metadata is the only dynamic part of an otherwise fully prerenderable route. Adding a dynamic marker is an explicit signal to Next.js that the page has intentional dynamic content streamed alongside the static shell, so the dynamic metadata is allowed.
@@ -131,7 +130,7 @@ This error fires specifically because the metadata is the only dynamic part of a
#### Add a dynamic marker component
Create a small component that calls [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) and renders nothing, wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). The page content remains prerenderable and only the marker is excluded from the prerender.
Create a small component that calls [`connection()`](/docs/app/api-reference/functions/connection) and renders nothing, wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). The page content remains prerenderable and only the marker is excluded from the prerender.
```jsx filename="app/page.js"
import { Suspense } from 'react'
@@ -160,7 +159,7 @@ export default function Page() {
}
```
Learn more: [`connection`](https://preview.nextjs.org/docs/app/api-reference/functions/connection).
Learn more: [`connection`](/docs/app/api-reference/functions/connection).
### Trade-off
@@ -170,23 +169,23 @@ The metadata and the dynamic marker run on every request, so the route cannot be
- The `DynamicMarker` must be wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). Without the boundary, the dynamic marker propagates up and the entire page is treated as blocking, surfacing the same blocking-route error this fix is meant to address.
- This pattern is intentionally verbose. If you find yourself adding a dynamic marker, reconsider whether the metadata can be cached instead. Most metadata doesn't need to be per-request.
- If the page already has a genuinely dynamic component (one that reads [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or uncached data inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary), you won't see this error. The page is already partially dynamic.
- If the page already has a genuinely dynamic component (one that reads [`cookies()`](/docs/app/api-reference/functions/cookies) or uncached data inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary), you won't see this error. The page is already partially dynamic.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateMetadata` and must be statically prerendered. The dynamic marker doesn't help here, because these routes don't have a page body where you can place a Suspense'd marker. If your root layout's `generateMetadata` depends on uncached data, [Cache the metadata](#cache-the-metadata) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateMetadata`.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+20 -21
View File
@@ -17,15 +17,14 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), [`generateMetadata()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata) or file-based metadata read a per-request value ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js expects metadata to be prerenderable when the rest of the route is. This route's metadata is blocked, but the rest of its content can be prerendered.
During [prerendering](/docs/app/glossary#prerendering), [`generateMetadata()`](/docs/app/api-reference/functions/generate-metadata) or file-based metadata read a per-request value ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js expects metadata to be prerenderable when the rest of the route is. This route's metadata is blocked, but the rest of its content can be prerendered.
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection)) in `generateMetadata()` have different fixes. See [Next.js encountered uncached data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-dynamic).
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](/docs/app/api-reference/functions/connection)) in `generateMetadata()` have different fixes. See [Next.js encountered uncached data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-dynamic).
The viewport equivalent is handled at [Runtime data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-runtime).
@@ -57,7 +56,7 @@ For errors in the page body rather than metadata, see [Next.js encountered runti
## Use static metadata
Choose this fix when the metadata values are known at build time and don't change per request. Replace [`generateMetadata()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata) with a static [`metadata`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata#metadata-object) export. The metadata is evaluated once during the build and included in every prerender.
Choose this fix when the metadata values are known at build time and don't change per request. Replace [`generateMetadata()`](/docs/app/api-reference/functions/generate-metadata) with a static [`metadata`](/docs/app/api-reference/functions/generate-metadata#metadata-object) export. The metadata is evaluated once during the build and included in every prerender.
### Patterns
@@ -76,11 +75,11 @@ export default function Page() {
}
```
Learn more: [Static metadata](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata#metadata-object).
Learn more: [Static metadata](/docs/app/api-reference/functions/generate-metadata#metadata-object).
#### Use `generateStaticParams` for per-param metadata
When metadata varies by route param (a blog post title, a product name), pair [`generateStaticParams`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-static-params) with [`generateMetadata`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata). Each param set is prerendered with its own metadata at build time.
When metadata varies by route param (a blog post title, a product name), pair [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) with [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata). Each param set is prerendered with its own metadata at build time.
```jsx filename="app/blog/[slug]/page.js"
export function generateStaticParams() {
@@ -95,7 +94,7 @@ export async function generateMetadata({ params }) {
}
```
Learn more: [`generateStaticParams`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-static-params).
Learn more: [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params).
### Trade-off
@@ -103,12 +102,12 @@ Static metadata can't reflect per-request values like the visitor's locale, A/B
### Gotchas
- File-based metadata (e.g. an [`icon.js`](/docs/app/api-reference/file-conventions/metadata/app-icons) or [`opengraph-image.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/metadata/opengraph-image) inside a dynamic segment) implicitly depends on `params`. If the segment is dynamic, Next.js treats the metadata function as dynamic too. Pair with [`generateStaticParams`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-static-params) or switch to a static file (e.g. `icon.png`).
- A [`template`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-metadata#template) in a parent layout's metadata applies at build time. It doesn't introduce a dynamic dependency.
- File-based metadata (e.g. an [`icon.js`](/docs/app/api-reference/file-conventions/metadata/app-icons) or [`opengraph-image.js`](/docs/app/api-reference/file-conventions/metadata/opengraph-image) inside a dynamic segment) implicitly depends on `params`. If the segment is dynamic, Next.js treats the metadata function as dynamic too. Pair with [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) or switch to a static file (e.g. `icon.png`).
- A [`template`](/docs/app/api-reference/functions/generate-metadata#template) in a parent layout's metadata applies at build time. It doesn't introduce a dynamic dependency.
## Mark the route as dynamic
Choose this fix when the metadata genuinely requires per-request data (a personalized title from a protected API, a theme color from a cookie) and a static export isn't feasible. Add a small component that calls [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), render `null` from it, and wrap it in [`<Suspense>`](https://react.dev/reference/react/Suspense).
Choose this fix when the metadata genuinely requires per-request data (a personalized title from a protected API, a theme color from a cookie) and a static export isn't feasible. Add a small component that calls [`await connection()`](/docs/app/api-reference/functions/connection), render `null` from it, and wrap it in [`<Suspense>`](https://react.dev/reference/react/Suspense).
This error fires specifically because the metadata is the only dynamic part of an otherwise fully prerenderable route. Adding a dynamic marker is an explicit signal to Next.js that the page has intentional dynamic content streamed alongside the static shell, so the dynamic metadata is allowed.
@@ -116,7 +115,7 @@ This error fires specifically because the metadata is the only dynamic part of a
#### Add a dynamic marker component
Create a small component that calls [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) and renders nothing, wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). The page content remains prerenderable and only the marker is excluded from the prerender.
Create a small component that calls [`connection()`](/docs/app/api-reference/functions/connection) and renders nothing, wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). The page content remains prerenderable and only the marker is excluded from the prerender.
```jsx filename="app/page.js"
import { Suspense } from 'react'
@@ -149,7 +148,7 @@ export default function Page() {
}
```
Learn more: [`connection`](https://preview.nextjs.org/docs/app/api-reference/functions/connection).
Learn more: [`connection`](/docs/app/api-reference/functions/connection).
### Trade-off
@@ -159,23 +158,23 @@ The metadata and the dynamic marker run on every request, so the route cannot be
- The `DynamicMarker` must be wrapped in [`<Suspense>`](https://react.dev/reference/react/Suspense). Without the boundary, the dynamic marker propagates up and the entire page is treated as blocking, surfacing the same blocking-route error this fix is meant to address.
- This pattern is intentionally verbose. If you find yourself adding a dynamic marker, reconsider whether the metadata can be cached instead. Most metadata doesn't need to be per-request.
- If the page already has a genuinely dynamic component (one that reads [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary), you won't see this error. The page is already partially dynamic.
- If the page already has a genuinely dynamic component (one that reads [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary), you won't see this error. The page is already partially dynamic.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateMetadata` and must be statically prerendered. The dynamic marker doesn't help here, because these routes don't have a page body where you can place a Suspense'd marker. If your root layout's `generateMetadata` depends on request data, [Use static metadata](#use-static-metadata) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateMetadata`.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+11 -12
View File
@@ -17,10 +17,9 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
A [Client Component](/docs/app/getting-started/server-and-client-components#using-client-components) called [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) inline during render, and the surrounding tree had no [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. Client Components are server-side rendered on first load, so Next.js can't bake an unpredictable value into the prerendered HTML. The SSR value won't match the value the client computes on hydration, so you need to choose: defer the value behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so SSR can stream it, or move the call into [`useEffect`](https://react.dev/reference/react/useEffect) (or an event handler) so it only runs on the client.
@@ -86,15 +85,15 @@ export function Avatar() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
### Trade-off
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Use [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading) for full-segment fallback or pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The component shows the fallback during SSR and the first paint. For above-the-fold UI this can be visible. Use [`loading.js`](/docs/app/api-reference/file-conventions/loading) for full-segment fallback or pick a fallback that matches the final layout so it doesn't cause a layout shift when the component hydrates. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) in any of them raises this same error. Use stable placeholder content.
- Any UI rendered as part of the prerender shell must be deterministic, including [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) in any of them raises this same error. Use stable placeholder content.
- The inner Client Component still runs during SSR, behind the boundary. If you need to guarantee the random value only runs in the browser, use [Move into effect or event handler](#move-into-effect-or-event-handler) instead.
- A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary only fixes the prerender/hydration mismatch, not client re-renders. If the component using [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) re-renders on the client (a parent state change, a context update), it produces a new value each time. To stabilize the value across re-renders, call [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) once in a [`useState`](https://react.dev/reference/react/useState) initializer or [`useRef`](https://react.dev/reference/react/useRef), or compute it on the server and pass it down as a prop.
@@ -181,7 +180,7 @@ Learn more: [`useRef`](https://react.dev/reference/react/useRef).
### Trade-off
The user sees the placeholder briefly before the real value. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The user sees the placeholder briefly before the real value. For interactions the wait is invisible, but for `useEffect`-based values there's a flash of the initial state. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -194,7 +193,7 @@ The user sees the placeholder briefly before the real value. For interactions th
### Suspend with `use(io())`
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
When the read genuinely needs to happen per visit and you can't move it to an effect or event, call [`io()`](/docs/app/api-reference/functions/io) from `next/cache` before the read with React's [`use`](https://react.dev/reference/react/use) hook. Client Components prerender on the server during SSR, where the read would otherwise be included in the static shell. `use(io())` suspends the prerender so the component is excluded from the shell and rendered on every request from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary.
```jsx filename="app/components/random-banner.js"
'use client'
@@ -222,17 +221,17 @@ export default function Page() {
}
```
Learn more: [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io).
Learn more: [`io`](/docs/app/api-reference/functions/io).
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `Math.random()` returns a different value on every render, so the prerender can't bake it into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `Math.random()` returns a different value on every render, so the prerender can't bake it into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
+21 -22
View File
@@ -17,13 +17,12 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a Server Component called [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake an unpredictable value into the prerendered HTML. The value at build time will differ from the value at runtime, so you need to choose: cache the value so it's stable, defer the call behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
During [prerendering](/docs/app/glossary#prerendering), a Server Component called [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't bake an unpredictable value into the prerendered HTML. The value at build time will differ from the value at runtime, so you need to choose: cache the value so it's stable, defer the call behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary so it runs per-request, or move it to the client.
Other unpredictable APIs ([`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now), [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID)) have parallel error pages: see [`Date.now()`](/docs/messages/blocking-prerender-current-time) and [crypto APIs](/docs/messages/blocking-prerender-crypto). The Client Component case is handled at [`Math.random()` in a Client Component](/docs/messages/blocking-prerender-random-client).
@@ -64,13 +63,13 @@ Other unpredictable APIs ([`Date.now()`](https://developer.mozilla.org/en-US/doc
## Generate on every request
Choose this fix when each request genuinely needs a different value. A [unique session ID](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy), a single-use nonce, an A/B test bucket: anything that has to be fresh per visitor. Add [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
Choose this fix when each request genuinely needs a different value. A [unique session ID](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy), a single-use nonce, an A/B test bucket: anything that has to be fresh per visitor. Add [`await connection()`](/docs/app/api-reference/functions/connection) before the call to tell Next.js the surrounding component is request-bound. The component is excluded from the prerender and streamed in from the nearest [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary on each request.
### Patterns
#### Use `await connection()` before the random call
Call [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection) before [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random). Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Call [`connection()`](/docs/app/api-reference/functions/connection) before [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random). Everything after the `await` is request-time. Wrap the component in [`<Suspense>`](https://react.dev/reference/react/Suspense) so the surrounding shell stays prerendered and only the dynamic part streams in.
Push the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary as close to the random read as possible. If the parent has cached content (a header, stats, navigation), isolate the random read in its own component so only that piece falls behind the boundary.
@@ -101,7 +100,7 @@ export async function RequestTrace() {
#### Alternative: `await io()`
Use [`io()`](https://preview.nextjs.org/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
Use [`io()`](/docs/app/api-reference/functions/io) from `next/cache` to keep the read out of the static shell. Unlike [`connection()`](/docs/app/api-reference/functions/connection), `io()` doesn't block prefetches and works inside `"use cache"` scopes and Client Components.
```jsx filename="app/dashboard/request-trace.js"
import { io } from 'next/cache'
@@ -113,27 +112,27 @@ export async function RequestTrace() {
}
```
Learn more: [`connection`](https://preview.nextjs.org/docs/app/api-reference/functions/connection), [`io`](https://preview.nextjs.org/docs/app/api-reference/functions/io), [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [`connection`](/docs/app/api-reference/functions/connection), [`io`](/docs/app/api-reference/functions/io), [Streaming](/docs/app/guides/streaming).
### Trade-off
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it can paint. Make sure the fallback approximates the final layout so it doesn't cause a layout shift when the value arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The route renders on every request. The shell still ships instantly because of the [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary, but the dynamic region waits on the server render before it can paint. Make sure the fallback approximates the final layout so it doesn't cause a layout shift when the value arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading), [`error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/error#global-error). Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error.
- Any UI rendered as part of the prerender shell must be deterministic. That includes [`<Suspense>`](https://react.dev/reference/react/Suspense) fallbacks, [`loading.js`](/docs/app/api-reference/file-conventions/loading), [`error.js`](/docs/app/api-reference/file-conventions/error), [`not-found.js`](/docs/app/api-reference/file-conventions/not-found), and [`global-error.js`](/docs/app/api-reference/file-conventions/error#global-error). Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) in any of them raises this same error.
- If [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) is being used as a unique ID for logging or correlation, consider an incrementing integer or [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) request scope. Those don't trigger the error at all because they aren't unpredictable from Next.js's point of view.
- Random values produced inside third-party packages will surface this error in your project code. The same fixes apply at the call site that consumes the value.
## Cache the random value
Choose this fix when one stable random value per build, deployment, or `cacheLife` window is acceptable. The classic case is a daily shuffle of items where the same shuffle is fine for every visitor that day. Move the [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) call into a function with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) as the first statement. Next.js evaluates the function once per cache key and reuses the result.
Choose this fix when one stable random value per build, deployment, or `cacheLife` window is acceptable. The classic case is a daily shuffle of items where the same shuffle is fine for every visitor that day. Move the [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) call into a function with [`use cache`](/docs/app/api-reference/directives/use-cache) as the first statement. Next.js evaluates the function once per cache key and reuses the result.
### Patterns
#### Cache the producer function
Wrap the random generation in its own function with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). The returned value is part of the cache entry, so every consumer sees the same random number until the cache is invalidated.
Wrap the random generation in its own function with [`use cache`](/docs/app/api-reference/directives/use-cache). The returned value is part of the cache entry, so every consumer sees the same random number until the cache is invalidated.
```jsx filename="app/page.js"
async function getRandomSeed() {
@@ -148,11 +147,11 @@ export default async function Page() {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache).
#### Control the rotation window with `cacheLife`
When you want the random value to rotate on a schedule (a daily featured item, an hourly shuffle), set a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile.
When you want the random value to rotate on a schedule (a daily featured item, an hourly shuffle), set a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile.
```jsx filename="app/page.js"
import { cacheLife } from 'next/cache'
@@ -164,7 +163,7 @@ async function getDailySeed() {
}
```
Learn more: [How to configure cache lifetimes](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife).
Learn more: [How to configure cache lifetimes](/docs/app/api-reference/functions/cacheLife).
### Trade-off
@@ -172,13 +171,13 @@ Every visitor in the cache window sees the same "random" value. That's the right
### Gotchas
- Inside a [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) scope, you can't call [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), which means you can't key the random value by request identity.
- Inside a [`use cache`](/docs/app/api-reference/directives/use-cache) scope, you can't call [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers), which means you can't key the random value by request identity.
- If the same random value is used in many places, hoist the cached function up so all consumers share the cache entry instead of producing a different cached value at each call site.
- If you cache a function and still see this error, the [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
- If you cache a function and still see this error, the [`cacheLife`](/docs/app/api-reference/functions/cacheLife) may be too short to prerender. See [Short-lived caches](#short-lived-caches).
### Short-lived caches
[`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](https://preview.nextjs.org/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
[`use cache`](/docs/app/api-reference/directives/use-cache) accepts a [`cacheLife`](/docs/app/api-reference/functions/cacheLife) profile. A short profile (such as `"seconds"` or `"minutes"`) whose `revalidate` is shorter than the prerender's effective lifetime prevents the value from being included in the prerender. The segment becomes a dynamic hole instead. The cache entry still helps the [Client Cache](/docs/app/glossary#client-cache) and protects upstream APIs, but the page falls back to streaming.
To keep the page prerendered, use a profile with a longer revalidate window such as `"default"` (15 minutes), `"hours"`, or `"days"`. If a short profile is intentional, treat the value as dynamic and use [Generate on every request](#generate-on-every-request) instead.
@@ -219,7 +218,7 @@ Learn more: [Client Components](/docs/app/getting-started/server-and-client-comp
### Trade-off
The first paint shows the SSR fallback or initial state, and the random value appears only after the browser hydrates the component. That's fine for UI flourishes but wrong for content that has to be in the prerendered HTML. See [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
The first paint shows the SSR fallback or initial state, and the random value appears only after the browser hydrates the component. That's fine for UI flourishes but wrong for content that has to be in the prerendered HTML. See [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) for techniques that eliminate the flash.
### Gotchas
@@ -229,11 +228,11 @@ The first paint shows the SSR fallback or initial state, and the random value ap
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Why `instant = false` doesn't clear this error
This error fires from the prerender, not from instant-navigation validation. `Math.random()` returns a different value on every render, so the prerender can't bake it into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
This error fires from the prerender, not from instant-navigation validation. `Math.random()` returns a different value on every render, so the prerender can't bake it into a static shell regardless of the segment's `instant` config or [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults). Use one of the fixes above.
## Related Insights
+33 -34
View File
@@ -17,17 +17,16 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), or [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional) was read outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't prerender any part of the tree that depends on a per-request value, so navigations to this route block instead of being [instant](https://preview.nextjs.org/docs/app/guides/instant-navigation).
During [prerendering](/docs/app/glossary#prerendering), [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), or [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) was read outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js can't prerender any part of the tree that depends on a per-request value, so navigations to this route block instead of being [instant](/docs/app/guides/instant-navigation).
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection)) have different fixes. See [Next.js encountered uncached data during prerendering](/docs/messages/blocking-prerender-dynamic).
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](/docs/app/api-reference/functions/connection)) have different fixes. See [Next.js encountered uncached data during prerendering](/docs/messages/blocking-prerender-dynamic).
This error can also appear during a client-side navigation when the data access sits inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary from a parent layout but that boundary is too high. It wraps the entire segment instead of only the dynamic part, so the navigation still blocks. Push the boundary closer to the data access so the rest of the segment stays in the [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell). See [Choosing where to place the boundary](#choosing-where-to-place-the-boundary).
This error can also appear during a client-side navigation when the data access sits inside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary from a parent layout but that boundary is too high. It wraps the entire segment instead of only the dynamic part, so the navigation still blocks. Push the boundary closer to the data access so the rest of the segment stays in the [static shell](/docs/app/glossary#static-shell). See [Choosing where to place the boundary](#choosing-where-to-place-the-boundary).
## Ways to fix this
@@ -55,7 +54,7 @@ This error can also appear during a client-side navigation when the data access
## Wrap in or move into Suspense
Choose this fix when the value really is per-request, but the page has parts that don't depend on it. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary lets the static shell ship instantly while the dynamic region [streams](https://preview.nextjs.org/docs/app/glossary#streaming) in once the request value resolves.
Choose this fix when the value really is per-request, but the page has parts that don't depend on it. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary lets the static shell ship instantly while the dynamic region [streams](/docs/app/glossary#streaming) in once the request value resolves.
### Patterns
@@ -80,7 +79,7 @@ export default function Page() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
#### Push the access down to the leaf
@@ -110,7 +109,7 @@ export async function UserHeader() {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
#### Pass `searchParams` without awaiting
@@ -140,11 +139,11 @@ export async function Results({ searchParams }) {
}
```
Learn more: [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional).
Learn more: [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional).
#### Forward `searchParams` as a promise chain
A variant of the previous pattern when you want to derive a value without awaiting at the top. Treat [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional) as a promise and `.then()` it to project the shape the child needs.
A variant of the previous pattern when you want to derive a value without awaiting at the top. Treat [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) as a promise and `.then()` it to project the shape the child needs.
```jsx filename="app/dashboard/map/page.js"
export default function Page({ searchParams }) {
@@ -156,11 +155,11 @@ export default function Page({ searchParams }) {
}
```
Learn more: [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional).
Learn more: [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional).
#### Use `loading.js` for the whole segment
When every component in the segment reads the same request value and there's nothing static to render above it, a [`loading.js`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading) file in the segment is the shorthand. Next.js wraps `{children}` of the layout in `<Suspense>` automatically.
When every component in the segment reads the same request value and there's nothing static to render above it, a [`loading.js`](/docs/app/api-reference/file-conventions/loading) file in the segment is the shorthand. Next.js wraps `{children}` of the layout in `<Suspense>` automatically.
```jsx filename="app/dashboard/loading.js"
export default function Loading() {
@@ -170,11 +169,11 @@ export default function Loading() {
> **Good to know**: A `loading.js` file wraps the segment's `{children}` in one Suspense boundary. Parent layouts above it still prerender, but everything inside the segment sits behind the fallback. If page-level content could be prerendered (a static intro, a known title), use explicit `<Suspense>` boundaries inside `page.js` around only the dynamic parts.
Learn more: [`loading.js` and instant loading states](https://preview.nextjs.org/docs/app/api-reference/file-conventions/loading).
Learn more: [`loading.js` and instant loading states](/docs/app/api-reference/file-conventions/loading).
### Trade-off
The shell ships immediately, but the user sees a loading state for the streamed region on every request. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The shell ships immediately, but the user sees a loading state for the streamed region on every request. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Choosing where to place the boundary
@@ -183,21 +182,21 @@ The location of the boundary controls what the user sees during the navigation:
- A high boundary (around the whole page) gives one loading state for everything. Less work to set up, but the user loses context about where they were going.
- A low boundary (around the specific component that reads the runtime API) keeps surrounding content visible and only shows a fallback for the per-request part. Preferred when the surrounding shell has cached content.
A useful rule: **push the boundary as low as possible** while keeping the fallback meaningful. The cached content above the boundary becomes part of the [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) on navigation. Wrapping individual pieces or wrapping the whole page in one boundary stream the same way, but a lower boundary keeps more prerendered content visible during the navigation. See [Maximizing the static shell](https://preview.nextjs.org/docs/app/getting-started/caching#streaming-uncached-data) for the canonical pattern.
A useful rule: **push the boundary as low as possible** while keeping the fallback meaningful. The cached content above the boundary becomes part of the [static shell](/docs/app/glossary#static-shell) on navigation. Wrapping individual pieces or wrapping the whole page in one boundary stream the same way, but a lower boundary keeps more prerendered content visible during the navigation. See [Maximizing the static shell](/docs/app/getting-started/caching#streaming-uncached-data) for the canonical pattern.
### Gotchas
- The fallback must be deterministic. Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) inside the fallback raises a separate [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- The fallback must be deterministic. Calling [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) or [`Date.now()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now) inside the fallback raises a separate [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) error during prerendering.
- Do not pass `{children}` through in the fallback. Child pages may include dynamic reads (for example, `/_not-found` calling [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers)) that propagate into what should be a static fallback. Render a placeholder that doesn't include `{children}`.
- If the failing route is `/_not-found` and you don't have a `not-found.tsx` file, the read is in the root layout. `/_not-found` is a real prerendered route that inherits the root layout, so a `cookies()` or `headers()` read there fails on the synthetic route too. Run `next build --debug-prerender` to confirm the originating file, and fix it at the layout, not by adding a `not-found.tsx`.
- Boundary placement affects client navigations between sibling routes differently than initial page loads. Validation surfaces this in the dev server and at build time. See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
- The function returned by [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) and [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) is async. Make sure the component reading them is async too, and `await` the call.
- Boundary placement affects client navigations between sibling routes differently than initial page loads. Validation surfaces this in the dev server and at build time. See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
- The function returned by [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers) is async. Make sure the component reading them is async too, and `await` the call.
- The `params` and `searchParams` props are also async promises. Treat them like any other awaited value when deciding where the boundary goes.
- Root-element attributes (`<html lang>`, `<html dir>`, `<html data-theme>`) can't be wrapped in `<Suspense>`. You can't suspend the document root, and a boundary inside `<html>` still leaves the attribute itself server-cookie-dependent. Move the read to a pre-paint client script per [Preventing flash before hydration](https://preview.nextjs.org/docs/app/guides/preventing-flash-before-hydration) and add `suppressHydrationWarning` on `<html>` so React doesn't flag the script's mutation as a mismatch.
- Root-element attributes (`<html lang>`, `<html dir>`, `<html data-theme>`) can't be wrapped in `<Suspense>`. You can't suspend the document root, and a boundary inside `<html>` still leaves the attribute itself server-cookie-dependent. Move the read to a pre-paint client script per [Preventing flash before hydration](/docs/app/guides/preventing-flash-before-hydration) and add `suppressHydrationWarning` on `<html>` so React doesn't flag the script's mutation as a mismatch.
## Allow blocking route
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Choose this fix when the route renders per-request and there's no useful static shell. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
### Patterns
@@ -214,11 +213,11 @@ export default async function Page() {
}
```
Learn more: [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation).
Learn more: [Ensuring instant navigations](/docs/app/guides/instant-navigation).
#### Opt the layout out
When the shared layout itself can't ship instantly (it reads [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) of its own), set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
When the shared layout itself can't ship instantly (it reads [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) of its own), set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
```jsx filename="app/dashboard/layout.js"
export const instant = false
@@ -228,11 +227,11 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use either pattern when:
- The route needs request-time data high in the tree to decide what to render (for example auth, tenant, or other gating in a layout), so there is no meaningful [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) worth showing first.
- The route needs request-time data high in the tree to decide what to render (for example auth, tenant, or other gating in a layout), so there is no meaningful [static shell](/docs/app/glossary#static-shell) worth showing first.
- You're migrating a route incrementally and want to defer the lifetime decision without changing how the page renders today.
Don't use this to dismiss the error. Choose [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) when feasible.
@@ -243,23 +242,23 @@ Navigations to this route are not instant. The user waits for the full server re
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+22 -23
View File
@@ -17,15 +17,14 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), [`generateViewport()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport) performed an uncached data access ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database call, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection)). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, viewport metadata can't be deferred behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary because it affects the initial page load. The page can't be prerendered, so navigations block instead of being [instant](https://preview.nextjs.org/docs/app/guides/instant-navigation).
During [prerendering](/docs/app/glossary#prerendering), [`generateViewport()`](/docs/app/api-reference/functions/generate-viewport) performed an uncached data access ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database call, [`await connection()`](/docs/app/api-reference/functions/connection)). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, viewport metadata can't be deferred behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary because it affects the initial page load. The page can't be prerendered, so navigations block instead of being [instant](/docs/app/guides/instant-navigation).
Request-bound reads ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)) in `generateViewport()` have different fixes. See [Next.js encountered runtime data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-runtime). The metadata equivalent is handled at [Uncached data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-dynamic). For errors in the page body rather than viewport, see [Next.js encountered uncached data during prerendering](/docs/messages/blocking-prerender-dynamic).
Request-bound reads ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)) in `generateViewport()` have different fixes. See [Next.js encountered runtime data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-runtime). The metadata equivalent is handled at [Uncached data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-dynamic). For errors in the page body rather than viewport, see [Next.js encountered uncached data during prerendering](/docs/messages/blocking-prerender-dynamic).
## Ways to fix this
@@ -53,9 +52,9 @@ Request-bound reads ([`cookies()`](https://preview.nextjs.org/docs/app/api-refer
## Cache the viewport data
Choose this fix when the viewport values come from an external source (database, CMS) but don't need to change on every request. Add the [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) directive as the first statement inside [`generateViewport()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport). Next.js caches the returned viewport object and includes it in the prerender.
Choose this fix when the viewport values come from an external source (database, CMS) but don't need to change on every request. Add the [`use cache`](/docs/app/api-reference/directives/use-cache) directive as the first statement inside [`generateViewport()`](/docs/app/api-reference/functions/generate-viewport). Next.js caches the returned viewport object and includes it in the prerender.
This fix does not apply to [`connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection). The point of `connection()` is to opt into per-request rendering, so caching it would defeat the purpose. Use [Allow blocking route](#allow-blocking-route) instead.
This fix does not apply to [`connection()`](/docs/app/api-reference/functions/connection). The point of `connection()` is to opt into per-request rendering, so caching it would defeat the purpose. Use [Allow blocking route](#allow-blocking-route) instead.
### Patterns
@@ -81,20 +80,20 @@ export default function RootLayout({ children }) {
}
```
Learn more: [Caching with `use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache).
Learn more: [Caching with `use cache`](/docs/app/api-reference/directives/use-cache).
### Trade-off
Freshness depends on the cache configuration. The viewport stays the same until [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) expires or [`cacheTag`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheTag) is invalidated.
Freshness depends on the cache configuration. The viewport stays the same until [`cacheLife`](/docs/app/api-reference/functions/cacheLife) expires or [`cacheTag`](/docs/app/api-reference/functions/cacheTag) is invalidated.
### Gotchas
- Inside a [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache) scope, you can't call [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) or [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers). If the viewport needs a request-bound value (a theme color from a cookie), use [Allow blocking route](#allow-blocking-route) instead.
- A short [`cacheLife`](https://preview.nextjs.org/docs/app/api-reference/functions/cacheLife) (a profile whose `revalidate` is shorter than the prerender's effective lifetime) prevents the viewport from being included in the prerender. Use a longer profile if you want the viewport included in the static shell.
- Inside a [`use cache`](/docs/app/api-reference/directives/use-cache) scope, you can't call [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers). If the viewport needs a request-bound value (a theme color from a cookie), use [Allow blocking route](#allow-blocking-route) instead.
- A short [`cacheLife`](/docs/app/api-reference/functions/cacheLife) (a profile whose `revalidate` is shorter than the prerender's effective lifetime) prevents the viewport from being included in the prerender. Use a longer profile if you want the viewport included in the static shell.
## Allow blocking route
Choose this fix when the viewport data is genuinely uncacheable. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Choose this fix when the viewport data is genuinely uncacheable. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Unlike page body content, viewport metadata can't be deferred behind [`<Suspense>`](https://react.dev/reference/react/Suspense) because it affects the initial HTML `<head>`. Making the viewport dynamic means the entire page navigation blocks.
@@ -102,7 +101,7 @@ Unlike page body content, viewport metadata can't be deferred behind [`<Suspense
#### Opt the layout out
Set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout that defines `generateViewport`. This allows that layout segment to block while descendant segments remain independently validated. Apply this to the nested layout that owns the dynamic viewport, not the root layout, so the opt-out is scoped to the affected segment.
Set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout that defines `generateViewport`. This allows that layout segment to block while descendant segments remain independently validated. Apply this to the nested layout that owns the dynamic viewport, not the root layout, so the opt-out is scoped to the affected segment.
```jsx filename="app/dashboard/layout.js"
import { db } from './db'
@@ -119,7 +118,7 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use this pattern when:
@@ -134,24 +133,24 @@ Navigations to this route are not instant. The user waits for the full server re
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateViewport` and must be statically prerendered. [`instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) opts the route out of validation but does not let those routes through, so the build still fails when they prerender. If your root layout's `generateViewport` depends on request data, [Cache the viewport data](#cache-the-viewport-data) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateViewport`.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateViewport` and must be statically prerendered. [`instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) opts the route out of validation but does not let those routes through, so the build still fails when they prerender. If your root layout's `generateViewport` depends on request data, [Cache the viewport data](#cache-the-viewport-data) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateViewport`.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+19 -20
View File
@@ -17,15 +17,14 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), [`generateViewport()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport) read a per-request value ([`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional)). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, viewport metadata can't be deferred behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary because it affects the initial page load. The page can't be prerendered, so navigations block instead of being [instant](https://preview.nextjs.org/docs/app/guides/instant-navigation).
During [prerendering](/docs/app/glossary#prerendering), [`generateViewport()`](/docs/app/api-reference/functions/generate-viewport) read a per-request value ([`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), [`params`](/docs/app/api-reference/file-conventions/page#params-optional), [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional)). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, viewport metadata can't be deferred behind a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary because it affects the initial page load. The page can't be prerendered, so navigations block instead of being [instant](/docs/app/guides/instant-navigation).
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](https://preview.nextjs.org/docs/app/api-reference/functions/connection)) in `generateViewport()` have different fixes. See [Next.js encountered uncached data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-dynamic). The metadata equivalent is handled at [Runtime data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-runtime). For errors in the page body rather than viewport, see [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch), database calls, [`await connection()`](/docs/app/api-reference/functions/connection)) in `generateViewport()` have different fixes. See [Next.js encountered uncached data in `generateViewport()`](/docs/messages/blocking-prerender-viewport-dynamic). The metadata equivalent is handled at [Runtime data in `generateMetadata()`](/docs/messages/blocking-prerender-metadata-runtime). For errors in the page body rather than viewport, see [Next.js encountered runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
## Ways to fix this
@@ -53,7 +52,7 @@ Uncached data accesses ([`fetch()`](https://developer.mozilla.org/en-US/docs/Web
## Use static viewport
Choose this fix when the viewport doesn't actually need the per-request data, either because the values are known at build time, or because the dependency on runtime data is accidental and can be refactored away. Replace [`generateViewport()`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport) with a static [`viewport`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport#the-viewport-object) export, or rewrite `generateViewport()` so it no longer reads [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies), [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers), or other request-bound APIs. The values are evaluated once during the build.
Choose this fix when the viewport doesn't actually need the per-request data, either because the values are known at build time, or because the dependency on runtime data is accidental and can be refactored away. Replace [`generateViewport()`](/docs/app/api-reference/functions/generate-viewport) with a static [`viewport`](/docs/app/api-reference/functions/generate-viewport#the-viewport-object) export, or rewrite `generateViewport()` so it no longer reads [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), or other request-bound APIs. The values are evaluated once during the build.
### Patterns
@@ -77,7 +76,7 @@ export default function RootLayout({ children }) {
}
```
Learn more: [Static viewport](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport#the-viewport-object).
Learn more: [Static viewport](/docs/app/api-reference/functions/generate-viewport#the-viewport-object).
### Trade-off
@@ -86,11 +85,11 @@ Static viewport can't reflect per-request values like a user's preferred theme c
### Gotchas
- The `viewport` export is typically set in the root layout. Changing a layout's viewport affects every route in its subtree.
- [`themeColor`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-viewport#themecolor) is the most common reason for a dynamic `generateViewport`. Consider whether a single static value covers all cases before resorting to a blocking route.
- [`themeColor`](/docs/app/api-reference/functions/generate-viewport#themecolor) is the most common reason for a dynamic `generateViewport`. Consider whether a single static value covers all cases before resorting to a blocking route.
## Allow blocking route
Choose this fix when the viewport genuinely requires per-request data (a theme color from a cookie, a user-preferred width) and a static export isn't feasible. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Choose this fix when the viewport genuinely requires per-request data (a theme color from a cookie, a user-preferred width) and a static export isn't feasible. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation. The page renders on every request and the navigation blocks until that render completes.
Unlike page body content, viewport metadata can't be deferred behind [`<Suspense>`](https://react.dev/reference/react/Suspense) because it affects the initial HTML `<head>`. Making the viewport dynamic means the entire page navigation blocks.
@@ -98,7 +97,7 @@ Unlike page body content, viewport metadata can't be deferred behind [`<Suspense
#### Opt the layout out
Set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout that defines `generateViewport`. This allows that layout segment to block while descendant segments remain independently validated. Apply this to the nested layout that owns the dynamic viewport, not the root layout, so the opt-out is scoped to the affected segment.
Set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout that defines `generateViewport`. This allows that layout segment to block while descendant segments remain independently validated. Apply this to the nested layout that owns the dynamic viewport, not the root layout, so the opt-out is scoped to the affected segment.
```jsx filename="app/dashboard/layout.js"
import { cookies } from 'next/headers'
@@ -117,7 +116,7 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use this pattern when:
@@ -132,25 +131,25 @@ Navigations to this route are not instant. The user waits for the full server re
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.
- If the dynamic viewport is the only reason the route blocks, consider whether a static default covers most users. A static `themeColor` with a client-side correction after hydration may give a better experience than blocking the entire navigation.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateViewport` and must be statically prerendered. [`instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) opts the route out of validation but does not let those routes through, so the build still fails when they prerender. If your root layout's `generateViewport` depends on request data, [Use static viewport](#use-static-viewport) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateViewport`.
- Framework-synthesized routes (`/_not-found`, `/_global-error`) inherit the root layout's `generateViewport` and must be statically prerendered. [`instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) opts the route out of validation but does not let those routes through, so the build still fails when they prerender. If your root layout's `generateViewport` depends on request data, [Use static viewport](#use-static-viewport) instead, or move to [`global-not-found.js`](/docs/app/api-reference/file-conventions/not-found#global-not-foundjs-experimental), which bypasses the root layout entirely and avoids inheriting its `generateViewport`.
## Verifying the fix
After applying a fix, reload the route and confirm the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary placed around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+19 -20
View File
@@ -17,15 +17,14 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During a [client-side navigation](https://preview.nextjs.org/docs/app/glossary#client-side-navigation), a [`<Link prefetch={true}>`](https://preview.nextjs.org/docs/app/api-reference/components/link) navigated to a route that has not enabled [Partial Prefetching](https://preview.nextjs.org/docs/app/glossary#partial-prefetching). With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, `prefetch={true}` is a legacy "full" prefetch that pulls down the route's dynamic data along with its [App Shell](https://preview.nextjs.org/docs/app/glossary#app-shell). This will lead to slower, more expensive prefetches.
During a [client-side navigation](/docs/app/glossary#client-side-navigation), a [`<Link prefetch={true}>`](/docs/app/api-reference/components/link) navigated to a route that has not enabled [Partial Prefetching](/docs/app/glossary#partial-prefetching). With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, `prefetch={true}` is a legacy "full" prefetch that pulls down the route's dynamic data along with its [App Shell](/docs/app/glossary#app-shell). This will lead to slower, more expensive prefetches.
Routes that opt into Partial Prefetching skip the dynamic data at prefetch time, leaving you free to choose when it loads: at navigation via [streaming](https://preview.nextjs.org/docs/app/glossary#streaming), ahead of time via [runtime prefetching](https://preview.nextjs.org/docs/app/guides/runtime-prefetching), or not at all. The check fires at navigation time, not prefetch time, so existing apps that have recently enabled Cache Components are not flooded with warnings for every `<Link prefetch={true}>` on the page.
Routes that opt into Partial Prefetching skip the dynamic data at prefetch time, leaving you free to choose when it loads: at navigation via [streaming](/docs/app/glossary#streaming), ahead of time via [runtime prefetching](/docs/app/guides/runtime-prefetching), or not at all. The check fires at navigation time, not prefetch time, so existing apps that have recently enabled Cache Components are not flooded with warnings for every `<Link prefetch={true}>` on the page.
## Ways to fix this
@@ -62,13 +61,13 @@ Routes that opt into Partial Prefetching skip the dynamic data at prefetch time,
## Opt into Partial Prefetching
Choose this fix when the target route has an [App Shell](https://preview.nextjs.org/docs/app/glossary#app-shell) with dynamic content below it. Opting into Partial Prefetching tells Next.js to prefetch only the App Shell and defer the dynamic data to navigation. Opt in per-route or app-wide, and from there layer on further prefetch optimizations.
Choose this fix when the target route has an [App Shell](/docs/app/glossary#app-shell) with dynamic content below it. Opting into Partial Prefetching tells Next.js to prefetch only the App Shell and defer the dynamic data to navigation. Opt in per-route or app-wide, and from there layer on further prefetch optimizations.
### Patterns
#### Per-route opt-in
Export [`prefetch`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/prefetch) from the page or layout of the route the link points at.
Export [`prefetch`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch) from the page or layout of the route the link points at.
```jsx filename="app/dashboard/page.js"
export const prefetch = 'partial'
@@ -80,7 +79,7 @@ export default function DashboardPage() {
#### App-wide opt-in
Set [`partialPrefetching`](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/partialPrefetching) to `true` in `next.config` to opt the whole app in.
Set [`partialPrefetching`](/docs/app/api-reference/config/next-config-js/partialPrefetching) to `true` in `next.config` to opt the whole app in.
```js filename="next.config.js"
module.exports = {
@@ -92,8 +91,8 @@ module.exports = {
`'partial'` prefetches only the App Shell, which is the route's static and cached content. Uncached dynamic data is no longer prefetched. To keep prefetching content that came down with `prefetch={true}`, work through two steps.
1. Cache it with [`use cache`](https://preview.nextjs.org/docs/app/api-reference/directives/use-cache). If the content doesn't depend on the URL, it gets included in the App Shell and that's enough.
2. If it depends on per-link runtime data (`params`, `searchParams`), it can't be included in the shared App Shell. With Partial Prefetching enabled, `prefetch={true}` opts the link into [runtime prefetching](https://preview.nextjs.org/docs/app/guides/runtime-prefetching), so the cached content is prefetched behind the runtime read.
1. Cache it with [`use cache`](/docs/app/api-reference/directives/use-cache). If the content doesn't depend on the URL, it gets included in the App Shell and that's enough.
2. If it depends on per-link runtime data (`params`, `searchParams`), it can't be included in the shared App Shell. With Partial Prefetching enabled, `prefetch={true}` opts the link into [runtime prefetching](/docs/app/guides/runtime-prefetching), so the cached content is prefetched behind the runtime read.
```jsx filename="app/dashboard/page.js"
import { Suspense } from 'react'
@@ -120,7 +119,7 @@ export default function DashboardPage({ searchParams }) {
}
```
Learn more: [Adopting Partial Prefetching](https://preview.nextjs.org/docs/app/guides/adopting-partial-prefetching).
Learn more: [Adopting Partial Prefetching](/docs/app/guides/adopting-partial-prefetching).
### Trade-off
@@ -128,7 +127,7 @@ The route's dynamic data isn't included in the prefetch. The user sees the App S
### Gotchas
- Partial Prefetching only works with [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled.
- Partial Prefetching only works with [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled.
- If the route doesn't have a clear App Shell (everything below the layout reads dynamic data), Partial Prefetching has nothing to prefetch and behaves the same as no prefetch. Move static content above the dynamic boundary first.
## Use the default prefetch
@@ -154,11 +153,11 @@ The link no longer forces a full prefetch. The user gets the App Shell (static a
### Gotchas
- Removing `prefetch={true}` does not disable prefetching. It falls back to the default. To disable prefetching entirely, use `prefetch={false}`.
- See [Adopting Partial Prefetching](https://preview.nextjs.org/docs/app/guides/adopting-partial-prefetching) for the full table of what each `<Link>` prop downloads under each configuration.
- See [Adopting Partial Prefetching](/docs/app/guides/adopting-partial-prefetching) for the full table of what each `<Link>` prop downloads under each configuration.
## Disable validation on this route
Choose this fix when you need the legacy full prefetch behavior and cannot adopt Partial Prefetching for the target route. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the target route opts it out of instant-navigation validation.
Choose this fix when you need the legacy full prefetch behavior and cannot adopt Partial Prefetching for the target route. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the target route opts it out of instant-navigation validation.
### Patterns
@@ -186,16 +185,16 @@ The link continues to do a full prefetch, including dynamic data, and the warnin
After applying a fix, navigate to the route and confirm the insight no longer appears in the dev overlay and the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
Depending on your [validation level](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), this may only surface in development.
Depending on your [validation level](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), this may only surface in development.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+24 -25
View File
@@ -17,17 +17,16 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During a [client-side navigation](https://preview.nextjs.org/docs/app/glossary#client-side-navigation), a Server Component read [`params`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#params-optional) or [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional) outside of a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. With [Partial Prefetching](https://preview.nextjs.org/docs/app/glossary#partial-prefetching) enabled, Next.js extracts one [App Shell](https://preview.nextjs.org/docs/app/glossary#app-shell) from the route ahead of the click, so every link to it reuses the same prefetch instead of fetching a fresh one per URL.
During a [client-side navigation](/docs/app/glossary#client-side-navigation), a Server Component read [`params`](/docs/app/api-reference/file-conventions/page#params-optional) or [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) outside of a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary. With [Partial Prefetching](/docs/app/glossary#partial-prefetching) enabled, Next.js extracts one [App Shell](/docs/app/glossary#app-shell) from the route ahead of the click, so every link to it reuses the same prefetch instead of fetching a fresh one per URL.
The `params` and `searchParams` props are [URL data](https://preview.nextjs.org/docs/app/glossary#url-data): they're specific to a single URL, so reading them outside a `<Suspense>` boundary ties the App Shell to one link. Next.js can no longer reuse it across links, and navigations to this route may not be instant.
The `params` and `searchParams` props are [URL data](/docs/app/glossary#url-data): they're specific to a single URL, so reading them outside a `<Suspense>` boundary ties the App Shell to one link. Next.js can no longer reuse it across links, and navigations to this route may not be instant.
The check runs when you load the route and when you navigate to it. The App Shell itself is only used for client-side navigations: the initial load has its own validation against the route's [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell), where the same read surfaces as [runtime data during prerendering](/docs/messages/blocking-prerender-runtime). For URL data read through client hooks like [`useSearchParams`](https://preview.nextjs.org/docs/app/api-reference/functions/use-search-params), see [URL data in a Client Component outside of Suspense](/docs/messages/blocking-prerender-client-hook).
The check runs when you load the route and when you navigate to it. The App Shell itself is only used for client-side navigations: the initial load has its own validation against the route's [static shell](/docs/app/glossary#static-shell), where the same read surfaces as [runtime data during prerendering](/docs/messages/blocking-prerender-runtime). For URL data read through client hooks like [`useSearchParams`](/docs/app/api-reference/functions/use-search-params), see [URL data in a Client Component outside of Suspense](/docs/messages/blocking-prerender-client-hook).
## Ways to fix this
@@ -55,7 +54,7 @@ The check runs when you load the route and when you navigate to it. The App Shel
## Wrap in or move into Suspense
Choose this fix when the URL-specific content can render after the navigation. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary keeps the read out of the App Shell, so every link still shares the same prefetch and only the wrapped region [streams](https://preview.nextjs.org/docs/app/glossary#streaming) in after the navigation.
Choose this fix when the URL-specific content can render after the navigation. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary keeps the read out of the App Shell, so every link still shares the same prefetch and only the wrapped region [streams](/docs/app/glossary#streaming) in after the navigation.
### Patterns
@@ -87,7 +86,7 @@ export async function Results({ searchParams }) {
}
```
Learn more: [`searchParams`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional).
Learn more: [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional).
#### Read `params` in the leaf that needs it
@@ -116,21 +115,21 @@ export async function ProductDetails({ params }) {
}
```
Learn more: [Streaming](https://preview.nextjs.org/docs/app/guides/streaming).
Learn more: [Streaming](/docs/app/guides/streaming).
### Trade-off
The shared parts of the route are prefetched, and the URL-dependent region streams in after navigation, so the user sees a fallback for that region. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](https://preview.nextjs.org/docs/app/guides/streaming#cls-cumulative-layout-shift).
The shared parts of the route are prefetched, and the URL-dependent region streams in after navigation, so the user sees a fallback for that region. Design the fallback so it approximates the final layout. A generic spinner causes a layout shift when content arrives. See [minimizing layout shift](/docs/app/guides/streaming#cls-cumulative-layout-shift).
### Gotchas
- [`cookies()`](https://preview.nextjs.org/docs/app/api-reference/functions/cookies) and [`headers()`](https://preview.nextjs.org/docs/app/api-reference/functions/headers) don't trigger this error, even outside `<Suspense>`. They vary per session, not per link, so the App Shell stays reusable across links. The initial load's [static shell](https://preview.nextjs.org/docs/app/glossary#static-shell) may still need them behind `<Suspense>`, and that requirement surfaces separately as [runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
- Making the route static with [`generateStaticParams`](https://preview.nextjs.org/docs/app/api-reference/functions/generate-static-params) does not resolve this error. A static param is still specific to one URL, so it can't be part of a prefetch shared across links.
- [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers) don't trigger this error, even outside `<Suspense>`. They vary per session, not per link, so the App Shell stays reusable across links. The initial load's [static shell](/docs/app/glossary#static-shell) may still need them behind `<Suspense>`, and that requirement surfaces separately as [runtime data during prerendering](/docs/messages/blocking-prerender-runtime).
- Making the route static with [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) does not resolve this error. A static param is still specific to one URL, so it can't be part of a prefetch shared across links.
- The `params` and `searchParams` props are promises. Passing the promise down without awaiting it keeps the rest of the route in the shared prefetch. Awaiting it above the boundary pulls the URL data back in.
## Allow blocking route
Choose this fix when the route genuinely can't provide a shared App Shell — it needs the URL data high in the tree to decide what to render — and you accept that navigations to it won't be instant. Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` marks the segment as allowed to block: it renders per navigation instead of reusing a shared prefetch.
Choose this fix when the route genuinely can't provide a shared App Shell — it needs the URL data high in the tree to decide what to render — and you accept that navigations to it won't be instant. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` marks the segment as allowed to block: it renders per navigation instead of reusing a shared prefetch.
### Patterns
@@ -147,11 +146,11 @@ export default async function Page({ searchParams }) {
}
```
Learn more: [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation).
Learn more: [Ensuring instant navigations](/docs/app/guides/instant-navigation).
#### Opt the layout out
When a shared layout reads the URL data, set [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
When a shared layout reads the URL data, set [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the layout. This allows that layout segment to block while descendant segments remain independently validated.
```jsx filename="app/dashboard/layout.js"
export const instant = false
@@ -161,7 +160,7 @@ export default function DashboardLayout({ children }) {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
Use either pattern when:
@@ -176,24 +175,24 @@ Navigations to this route are not instant. Without an App Shell, it renders per
### Gotchas
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- Allowing the route to block does not disable [Partial Prefetching](https://preview.nextjs.org/docs/app/glossary#partial-prefetching) or prefetching. It only exempts the segment from instant-navigation validation.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts only the segment that exports it out. Descendant segments are still validated by the global default.
- Allowing the route to block does not disable [Partial Prefetching](/docs/app/glossary#partial-prefetching) or prefetching. It only exempts the segment from instant-navigation validation.
- `instant = false` allows the route to block for all instant-navigation checks, not only this one. That includes [runtime data during prerendering](/docs/messages/blocking-prerender-runtime) errors and [unrendered segment](/docs/messages/instant-unrendered-segment) warnings for the route.
## Verifying the fix
After applying a fix, navigate to the route and confirm the insight no longer appears in the dev overlay and the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation. Depending on your [validation level](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), the insight may only surface in development.
After applying a fix, navigate to the route and confirm the insight no longer appears in the dev overlay and the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation. Depending on your [validation level](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), the insight may only surface in development.
In [`next dev`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](https://preview.nextjs.org/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
In [`next dev`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay points at the failing component with file paths and line numbers. When working from a build instead, the default [`next build`](/docs/app/api-reference/cli/next#next-build-options) output is more abbreviated. Run `next build --debug-prerender` for full user-frame stack traces and `next build --debug-build-paths /dashboard /settings` to iterate on specific routes.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights
+13 -14
View File
@@ -17,13 +17,12 @@ kind: insight
This Insight is part of the [Instant
Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature
introduced in Next.js 16.3. If you're new to it, start with the [Ensuring
instant
navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation)
guide for an overview of what instant navigations are and how Next.js
validates them, then come back here for the specific fix.
instant navigations](/docs/app/guides/instant-navigation) guide for an
overview of what instant navigations are and how Next.js validates them, then
come back here for the specific fix.
</div>
During [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering), a segment in the route tree was dropped from rendering. With [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js validates that every segment can produce an [instant navigation](https://preview.nextjs.org/docs/app/guides/instant-navigation). When a segment is not rendered, that validation can't run and issues that would prevent instant navigation go undetected.
During [prerendering](/docs/app/glossary#prerendering), a segment in the route tree was dropped from rendering. With [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) enabled, Next.js validates that every segment can produce an [instant navigation](/docs/app/guides/instant-navigation). When a segment is not rendered, that validation can't run and issues that would prevent instant navigation go undetected.
This typically happens when a layout conditionally omits `{children}` or a parallel route slot is not rendered.
@@ -130,7 +129,7 @@ export default async function DashboardPage() {
}
```
Learn more: [Authentication](https://preview.nextjs.org/docs/app/guides/authentication).
Learn more: [Authentication](/docs/app/guides/authentication).
### Trade-off
@@ -143,7 +142,7 @@ The segment is always in the render tree, which means Next.js validates it on ev
## Skip validation on the segment
Choose this fix when the segment is intentionally not rendered in certain states (a modal that only appears on interaction, a slot gated by authentication). Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the dropped segment tells Next.js to skip validation for it.
Choose this fix when the segment is intentionally not rendered in certain states (a modal that only appears on interaction, a slot gated by authentication). Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` on the dropped segment tells Next.js to skip validation for it.
### Patterns
@@ -159,7 +158,7 @@ export default function ModalPage() {
}
```
Learn more: [Route segment `instant` config](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant).
Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).
### Trade-off
@@ -168,22 +167,22 @@ The segment is exempt from instant-navigation validation. If it has issues that
### Gotchas
- The export must be on the **dropped segment's own file** (page or layout), not on a parent. The framework walks top-down and the first explicit config wins.
- Setting [`instant`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` does not disable [prerendering](https://preview.nextjs.org/docs/app/glossary#prerendering). The segment still prerenders if it can. It only disables the validation error.
- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` does not disable [prerendering](/docs/app/glossary#prerendering). The segment still prerenders if it can. It only disables the validation error.
## Verifying the fix
After applying a fix, navigate to the route and confirm the insight no longer appears in the dev overlay and the page immediately paints meaningful UI, with any `<Suspense>` fallbacks covering only the regions that stream in. A [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary around the whole page body can pass validation with an empty shell, which defeats the point of an instant navigation.
Depending on your [validation level](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), this may only surface in development.
Depending on your [validation level](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults), this may only surface in development.
## Don't want this validation?
Instant-navigation validation runs by default in [Cache Components](https://preview.nextjs.org/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
Instant-navigation validation runs by default in [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and is what surfaces this error.
- **One segment**: add [`export const instant = false`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.
See [Ensuring instant navigations](https://preview.nextjs.org/docs/app/guides/instant-navigation) for the full model.
See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.
## Related Insights