Unify allow-runtime with Partial Prefetching (#96106)

Removes the "allow-runtime" prefetch config, and turns its behavior on
implicitly wherever Partial Prefetching is enabled.

The original motivation for "allow-runtime" was to give apps more
control over server costs triggered by prefetches. Until a route
explicitly opts in, prefetches would only be served from the CDN, not
from the server. The problem, though, was it was very confusing to know
when to add or remove this configuration. The incentive for many apps
was to add it everywhere, with no clear signal for when to remove it.

Our updated thinking is that Partial Prefetching itself already provides
sufficient protection against runaway prefetching costs: per-link
prefetches only happen on Link components that explicitly opt in with
the prefetch prop.

The optimizations landed earlier in this stack also make allow-runtime
less necessary: on pages where all the content is statically renderable,
prefetches are served from the static cache and no runtime request is
ever issued; only a page that accesses non-static data is prefetched at
runtime.

The upshot of this decision is that runtime versus static becomes an
internal optimization; the same content gets prefetched regardless of
whether or how Next.js is able to optimize it.
This commit is contained in:
Andrew Clark
2026-07-28 11:51:29 -04:00
committed by GitHub
parent dfa7f4f713
commit 3de2d1a213
183 changed files with 435 additions and 600 deletions
@@ -535,7 +535,7 @@ Read the [Instant navigation guide](/docs/app/guides/instant-navigation) for exa
With [Partial Prefetching](/docs/app/api-reference/config/next-config-js/partialPrefetching) enabled, the router prefetches each route's [App Shell](/docs/app/glossary#app-shell) by default, which already includes static content and the session data derived from `cookies()` and `headers()`. Runtime prefetching extends the prefetch with **URL data**: the `searchParams` and dynamic `params` that vary per destination link.
With [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch) on a route, Next.js renders that route's component tree again at prefetch time, this time with the destination URL resolved. The same rules apply, but more of the tree resolves now that its `searchParams` and `params` are in scope:
With [`<Link prefetch={true}>`](/docs/app/api-reference/components/link#prefetch) pointing at a [Partial Prefetching](/docs/app/api-reference/config/next-config-js/partialPrefetching) route, Next.js renders that route's component tree again at prefetch time, this time with the destination URL resolved. The same rules apply, but more of the tree resolves now that its `searchParams` and `params` are in scope:
- [`use cache`](#usage) called with values extracted from runtime APIs (passed as arguments) joins the runtime prerender
- [`use cache: private`](/docs/app/api-reference/directives/use-cache-private) executes on the server, reads runtime data directly, and caches the result in the browser, joining the runtime prerender
@@ -543,13 +543,11 @@ With [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/rou
This generates a **runtime prerender** that extends past the static shell with content the destination URL unlocks. Because it happens during the prefetch, the navigation has nothing to wait on. The cost is a server invocation per prefetchable link.
For example, take a search page that reads `searchParams` from the URL and opts into runtime prefetching:
For example, take a search page that reads `searchParams` from the URL:
```tsx filename="app/search/page.tsx"
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
export default function SearchPage(props: PageProps<'/search'>) {
return (
<Suspense fallback={<p>Loading results...</p>}>
@@ -14,7 +14,7 @@ related:
[Partial Prefetching](/docs/app/glossary#partial-prefetching) changes what a `<Link>` downloads for a Cache Components route. With Partial Prefetching enabled, a `<Link>` prefetches the route's [App Shell](/docs/app/glossary#app-shell): its static content and the cached content that doesn't depend on the URL. Next.js builds one App Shell per route and reuses it for every link to that route, rather than prefetching each link separately as it did before.
To prefetch more than the App Shell, a route can opt into [runtime prefetching](/docs/app/guides/runtime-prefetching) with [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime). The prefetch then also resolves the per-link runtime data (`params`, `searchParams`, and the full URL) and the cached content behind it.
To prefetch more than the App Shell, a link can opt into [runtime prefetching](/docs/app/guides/runtime-prefetching) with [`<Link prefetch={true}>`](/docs/app/api-reference/components/link#prefetch). The prefetch then also resolves the per-link runtime data (`params`, `searchParams`, and the full URL) and the cached content behind it.
Along the way, Next.js surfaces [instant navigation](/docs/app/guides/instant-navigation) insights in development, naming the link or route to change.
@@ -317,7 +317,7 @@ The per-route `prefetch = 'partial'` exports are now redundant. Remove them in o
npx @next/codemod@canary remove-partial-prefetch ./app
```
The codemod removes only the `'partial'` value and leaves other values such as `prefetch = 'allow-runtime'` in place.
The codemod removes only the `'partial'` value and leaves other values such as `prefetch = 'force-disabled'` in place.
## Auditing routes for URL data
@@ -433,7 +433,7 @@ Everything outside the boundary stays in the shared App Shell, and only the URL-
## Prefetching URL data
Content that depends on [URL data](/docs/app/glossary#url-data) (`params`, `searchParams`) can't be included in the shared App Shell, so it streams in after navigation. [Runtime prefetching](/docs/app/guides/runtime-prefetching) resolves it ahead of the click for links with `prefetch={true}`, at the cost of a server invocation per prefetchable link. To opt a route in, cache the content behind the read with [`use cache`](/docs/app/api-reference/directives/use-cache) so it can resolve at prefetch time, then add [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime). For a search page that reads `searchParams`:
Content that depends on [URL data](/docs/app/glossary#url-data) (`params`, `searchParams`) can't be included in the shared App Shell, so it streams in after navigation. [Runtime prefetching](/docs/app/guides/runtime-prefetching) resolves it ahead of the click for links with `prefetch={true}`, at the cost of a server invocation per prefetchable link. To make the content resolvable at prefetch time, cache it behind the read with [`use cache`](/docs/app/api-reference/directives/use-cache). For a search page that reads `searchParams`:
```tsx filename="app/search/page.tsx" switcher
// Before - the results stream in after navigation
@@ -487,8 +487,6 @@ export default function Page({ searchParams }) {
// After - the results are cached and prefetched behind the resolved searchParams
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
async function getResults(query: string) {
'use cache'
const res = await fetch(`https://api.example.com/search?q=${query}`)
@@ -515,8 +513,6 @@ export default function Page({ searchParams }: PageProps<'/search'>) {
// After - the results are cached and prefetched behind the resolved searchParams
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
async function getResults(query) {
'use cache'
const res = await fetch(`https://api.example.com/search?q=${query}`)
+1 -1
View File
@@ -64,7 +64,7 @@ To prefetch the page content alongside the shell for a specific link, set [`pref
</Link>
```
To prefetch with the user's session (cookies, headers, the full URL), opt the destination segment into [runtime prefetching](/docs/app/guides/runtime-prefetching) with `export const prefetch = 'allow-runtime'`.
With Partial Prefetching enabled, `prefetch={true}` also opts the link into [runtime prefetching](/docs/app/guides/runtime-prefetching), which resolves the per-link URL data (`params`, `searchParams`, the full URL) ahead of the click.
### Validate instant navigation
+2 -10
View File
@@ -738,17 +738,9 @@ Every Server Function follows the same shape: run the write, then call `updateTa
> **Good to know:** Partial Prefetching ships in Next.js 16.3 and later with [`partialPrefetching: true`](/docs/app/api-reference/config/next-config-js/partialPrefetching) set in `next.config.ts`. See the [Adopting Partial Prefetching guide](/docs/app/guides/adopting-partial-prefetching) for the migration path, plus the [Runtime Prefetching](/docs/app/guides/runtime-prefetching) and [Instant Navigation](/docs/app/guides/instant-navigation) guides for the full picture.
Under [Partial Prefetching](/docs/app/guides/adopting-partial-prefetching), [`<Link>`](/docs/app/api-reference/components/link) prefetches the destination's [App Shell](/docs/app/glossary#app-shell) (static and cached content) by default. Pulling content that depends on request data ahead of the click means opting the route into runtime prefetching.
Under [Partial Prefetching](/docs/app/guides/adopting-partial-prefetching), [`<Link>`](/docs/app/api-reference/components/link) prefetches the destination's [App Shell](/docs/app/glossary#app-shell) (static and cached content) by default. Pulling content that depends on request data ahead of the click means opting the link into runtime prefetching.
If the destination's cached reads depend on request data (cookies, headers, `searchParams`, or `params` not resolved by [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params)), opt the route into [runtime prefetching](/docs/app/guides/runtime-prefetching) with the [`prefetch`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch) segment export so those reads resolve at prefetch time:
```tsx filename="app/task/[id]/page.tsx"
export const prefetch = 'allow-runtime'
export default function TaskPage({ params }) {
// …
}
```
If the destination's cached reads depend on request data (cookies, headers, `searchParams`, or `params` not resolved by [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params)), opt the link into [runtime prefetching](/docs/app/guides/runtime-prefetching) with `prefetch={true}` so those reads resolve at prefetch time:
```tsx filename="features/task/components/task-card.tsx"
<Link href={`/task/${id}`} prefetch={true}>
+9 -11
View File
@@ -43,7 +43,7 @@ It also assumes your route is already structured for instant navigation. If it i
## What runtime prefetching does
Runtime prefetching takes two opt-ins: links to the route use `<Link prefetch={true}>`, and the destination route sets [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime).
Runtime prefetching is opted into per link with `<Link prefetch={true}>`. Any destination with [Partial Prefetching](/docs/app/api-reference/config/next-config-js/partialPrefetching) enabled supports it — globally via `partialPrefetching`, or per segment with [`prefetch = 'partial'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#partial).
A user on `/` sees links to `/search?q=react` and `/search?q=next`, each opting in with `prefetch={true}`:
@@ -69,8 +69,6 @@ The destination renders a static heading and a `<Results>` list whose contents d
```tsx filename="app/search/page.tsx"
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
export default function SearchPage({ searchParams }: PageProps<'/search'>) {
return (
<>
@@ -97,15 +95,15 @@ async function search(q: string) {
}
```
Without `prefetch = 'allow-runtime'`, the App Shell renders `<h1>` and shows the `<Results>` fallback. The query resolves after the click and streams the results in.
Without `prefetch={true}`, the App Shell renders `<h1>` and shows the `<Results>` fallback. The query resolves after the click and streams the results in.
With [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch) on the route, the router prefetches a prerender that resolves `<Results>` before the click. The `q` value comes from the link's URL, known at prefetch time, and the cached `search(q)` fills in from there. On the click, the results render immediately, with no fallback.
With `prefetch={true}` on the link, the router prefetches a prerender that resolves `<Results>` before the click. The `q` value comes from the link's URL, known at prefetch time, and the cached `search(q)` fills in from there. On the click, the results render immediately, with no fallback.
The prerender advances through anything static or cached, then stops at uncached reads and falls back to the surrounding `<Suspense>` boundary. That boundary is already in place from [structuring the route for instant navigation](/docs/app/guides/instant-navigation).
More of the page is rendered before the user clicks, with fewer loading states.
Generating it costs **a server invocation per prefetchable link**, so it is opt-in per route.
Generating it costs **a server invocation per prefetchable link**, so it is opt-in per link. On pages where all the content is statically renderable, Next.js serves the prefetch from the static cache instead; a page that accesses non-static data is prefetched at runtime.
> **Good to know:** A cold cache (first visit, or after expiration) means the server still has to compute the cached result. Users may see a loading spinner on that first navigation. Subsequent navigations are instant as long as the cache is warm.
@@ -113,7 +111,7 @@ Like `searchParams`, `params` needs a `<Suspense>` boundary, even when the value
## Session data resolves in the shell
Runtime prefetching, via `allow-runtime`, is for URL data. Session data is handled separately: a route that reads `cookies()` or `headers()`, including through `"use cache: private"`, gets an App Shell that includes its session data, cached per session on the client and ready on navigation without `allow-runtime`.
Runtime prefetching is for URL data. Session data is handled separately: a route that reads `cookies()` or `headers()`, including through `"use cache: private"`, gets an App Shell that includes its session data, cached per session on the client and ready on navigation without a per-link runtime prefetch.
A lookup based on session data needs a cache lifetime, the same way `search(q)` did for the URL. Take a dashboard nav that reads a cookie, then looks up content based on it:
@@ -201,7 +199,7 @@ Use it on routes where:
- That part of the tree has a known cache lifetime (it can be expressed with `"use cache"` or `"use cache: private"`)
- The traffic justifies the per-link server invocation
Skip it when the prefetch can't produce a better UI than the App Shell. Each visible `<Link prefetch={true}>` to a route with `'allow-runtime'` makes its own request to the server to render that link, and that cost only pays off if more of the page is ready before the click:
Skip it when the prefetch can't produce a better UI than the App Shell. Each visible `<Link prefetch={true}>` can wake a server, and that cost only pays off if more of the page is ready before the click:
- The route has little or no URL-data dependency. The App Shell already makes the navigation instant.
- The dependent content has to be fresh on every request. The prerender stops at the same `<Suspense>` fallback, so the user sees the same UI either way.
@@ -209,10 +207,10 @@ Skip it when the prefetch can't produce a better UI than the App Shell. Each vis
A per-link runtime prefetch is best-effort. It only helps the navigations where it completes before the click. On a slow connection, on a feed of many links, or on a direct visit, it may not be ready when the user navigates, and the navigation falls back to the App Shell. That shell is the reliable baseline, and runtime prefetching only layers on top when it arrives in time.
When many links to an `allow-runtime` route are visible at once, such as a grid of cards, each `<Link prefetch={true}>` prefetches that link's content as it enters the viewport, so the grid makes one such server request per card. Prefetch on intent instead: a [hover-triggered prefetch](/docs/app/guides/prefetching#hover-triggered-prefetch) fetches only the links the user is likely to click. The default `<Link>` (without `prefetch={true}`) prefetches just the App Shell, so it doesn't carry this cost.
When many links to a route are visible at once, such as a grid of cards, each `<Link prefetch={true}>` prefetches that link's content as it enters the viewport, so the grid makes one such server request per card. Prefetch on intent instead: a [hover-triggered prefetch](/docs/app/guides/prefetching#hover-triggered-prefetch) fetches only the links the user is likely to click. The default `<Link>` (without `prefetch={true}`) prefetches just the App Shell, so it doesn't carry this cost.
| | App Shell | Per-link runtime prefetch with `allow-runtime` |
| ------- | ------------------------------------------- | ---------------------------------------------- |
| | App Shell | Per-link runtime prefetch with `prefetch={true}` |
| ------- | ------------------------------------------- | ------------------------------------------------ |
| Scope | One per route | One per visible `<Link prefetch={true}>` |
| Content | Route's rendered output minus per-link data | Same, plus per-link URL data resolved |
| Cost | Bounded by route count | Bounded by visible-link count |
+1 -1
View File
@@ -98,7 +98,7 @@ See the [Migrating to Cache Components](/docs/app/guides/migrating-to-cache-comp
npx @next/codemod@canary remove-partial-prefetch ./app
```
This codemod removes `export const prefetch = 'partial'` from `app/**/{page,layout}` files, so you can enable [`partialPrefetching`](/docs/app/api-reference/config/next-config-js/partialPrefetching) globally and then drop the now-redundant per-route opt-ins. It removes only the `'partial'` value and leaves other values such as `prefetch = 'allow-runtime'` in place.
This codemod removes `export const prefetch = 'partial'` from `app/**/{page,layout}` files, so you can enable [`partialPrefetching`](/docs/app/api-reference/config/next-config-js/partialPrefetching) globally and then drop the now-redundant per-route opt-ins. It removes only the `'partial'` value and leaves other values such as `prefetch = 'force-disabled'` in place.
```diff filename="app/products/[slug]/page.tsx"
- export const prefetch = 'partial'
@@ -300,7 +300,7 @@ Prefetching happens when a `<Link />` component enters the user's viewport (init
The following values can be passed to the `prefetch` prop:
- **`"auto"` or `null` (default)**: Prefetch behavior depends on whether the route is static or dynamic. For static routes, the full route will be prefetched (including all its data). For dynamic routes, the partial route down to the nearest segment with a [`loading.js`](/docs/app/api-reference/file-conventions/loading#instant-loading-states) boundary will be prefetched.
- **`true`**: The full route is prefetched for both static and dynamic routes. With [Partial Prefetching](/docs/app/guides/adopting-partial-prefetching) enabled, the prefetch is the [App Shell](/docs/app/glossary#app-shell); on a route that opts into [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime), it also includes the per-link runtime data and the cached content behind it.
- **`true`**: The full route is prefetched for both static and dynamic routes. With [Partial Prefetching](/docs/app/guides/adopting-partial-prefetching) enabled, the prefetch is the [App Shell](/docs/app/glossary#app-shell) plus the per-link runtime data and the cached content behind it — see [Runtime prefetching](/docs/app/guides/runtime-prefetching).
- `false`: Prefetching will never happen both on entering the viewport and on hover.
> **With Partial Prefetching enabled** ([`partialPrefetching: true`](/docs/app/api-reference/config/next-config-js/partialPrefetching)): the default changes. `auto` prefetches the per-route [App Shell](/docs/app/glossary#app-shell) (the route's static and cached content) instead of the full page. See [Adopting Partial Prefetching](/docs/app/guides/adopting-partial-prefetching) for the full behavior change.
@@ -15,10 +15,10 @@ The `prefetch` route segment config controls how a segment is prefetched during
>
> - The `prefetch` export only works when [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled.
> - `prefetch` cannot be used when the segment is a Client Component.
> - The meaningful values to set are `'allow-runtime'`, `'partial'`, and `'force-disabled'`. `'auto'` is the default and is equivalent to omitting the export; don't write `prefetch = 'auto'` explicitly.
> - The meaningful values to set are `'partial'` and `'force-disabled'`. `'auto'` is the default and is equivalent to omitting the export; don't write `prefetch = 'auto'` explicitly.
```tsx filename="layout.tsx | page.tsx" switcher
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return <div>...</div>
@@ -26,7 +26,7 @@ export default function Page() {
```
```jsx filename="layout.js | page.js" switcher
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return <div>...</div>
@@ -35,22 +35,16 @@ export default function Page() {
## Options
### `'allow-runtime'`
Allows Next.js to prefetch this segment at runtime. Setting this option lets the server render a fresh response that resolves per-link runtime data: `params`, `searchParams`, and the full URL. Cookies and headers don't need `'allow-runtime'`. The framework already includes them in the [App Shell](/docs/app/glossary#app-shell) when the route reads them. Use this for personalized content when the latency and cost of runtime prefetching are appropriate for the segment.
> **Good to know**: When Next.js runtime-prefetches a segment, all downstream segments are included in the same runtime prefetch request. Segments deeper in the tree that are configured with `'force-disabled'` will still be prefetched as part of the runtime response.
```tsx filename="page.tsx"
export const prefetch = 'allow-runtime'
```
### `'partial'`
Opts the segment into [Partial Prefetching](/docs/app/guides/adopting-partial-prefetching) without enabling the global [`partialPrefetching`](/docs/app/api-reference/config/next-config-js/partialPrefetching) flag. A `<Link>` pointing at a segment with `prefetch = 'partial'` loads the per-route [App Shell](/docs/app/glossary#app-shell) instead of the legacy full prefetch. Set this on the destination, not the link.
For links that opt into a wider prefetch with [`<Link prefetch={true}>`](/docs/app/api-reference/components/link#prefetch), Next.js may also prefetch the segment at runtime: the server renders a fresh response that resolves per-link runtime data — `params`, `searchParams`, and the full URL. On pages where all the content is statically renderable, Next.js serves prefetches from the static cache; if a page accesses non-static data, it's prefetched at runtime.
Use this for incremental adoption when you can't enable `partialPrefetching` for the entire app at once. Once every route in scope has `prefetch = 'partial'`, enable the global flag and remove the per-route exports.
> **Good to know**: When Next.js runtime-prefetches a segment, all downstream segments are included in the same runtime prefetch request. Segments deeper in the tree that are configured with `'force-disabled'` will still be prefetched as part of the runtime response.
```tsx filename="page.tsx"
export const prefetch = 'partial'
```
@@ -67,20 +61,19 @@ A prefetch starts with a `<Link>` that expresses intent (should this destination
A destination can't know which links target it, so the segment config caps what any `<Link prefetch={true}>` pulls:
- [`'allow-runtime'`](#allow-runtime): App Shell plus per-link runtime data (`params`, `searchParams`, the full URL).
- [`'partial'`](#partial): App Shell only.
- [`'partial'`](#partial): App Shell for default links; a `<Link prefetch={true}>` additionally resolves per-link runtime data (`params`, `searchParams`, the full URL).
- [`'force-disabled'`](#force-disabled): skip segment data entirely.
`<Link prefetch={false}>` skips prefetching at the link level regardless of how the destination is configured.
> **Good to know**: A prefetch may be served from a CDN cache, reuse a cached App Shell, or run a fresh server render. Wider prefetches lean toward fresh server work and cost more server CPU per page view.
> **Good to know**: On pages where all the content is statically renderable, Next.js serves prefetches from the static cache (or a CDN). If a page accesses non-static data like cookies or headers, it's prefetched at runtime with a fresh server render, which costs server CPU per page view.
## TypeScript
```tsx
type Prefetch = 'auto' | 'allow-runtime' | 'partial' | 'force-disabled'
type Prefetch = 'auto' | 'partial' | 'force-disabled'
export const prefetch: Prefetch = 'allow-runtime'
export const prefetch: Prefetch = 'partial'
```
## Version History
@@ -52,7 +52,7 @@ The pattern is similar to per-route code splitting in single-page apps: one arti
> **Good to know**: Routes that read `cookies()` or `headers()` produce an App Shell that includes session data. The framework auto-detects this and caches the shell per session on the client.
A link can ask for more than the App Shell with [`<Link prefetch={true}>`](/docs/app/api-reference/components/link#prefetch). If the destination opts into [`prefetch = 'allow-runtime'`](/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime), the prefetch also resolves per-link runtime data like `params`, `searchParams`, and the full URL.
A link can ask for more than the App Shell with [`<Link prefetch={true}>`](/docs/app/api-reference/components/link#prefetch): the prefetch also resolves per-link runtime data like `params`, `searchParams`, and the full URL. See [Runtime prefetching](/docs/app/guides/runtime-prefetching).
> **Good to know**: If you use `<Link prefetch={true}>` to a route that hasn't opted into Partial Prefetching, a dev console error suggests enabling `partialPrefetching` app-wide or `prefetch = 'partial'` on the segment. The [dev warning Insight](/docs/messages/instant-link-prefetch-partial) covers each fix in detail.
+2 -2
View File
@@ -93,12 +93,12 @@ 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. Opt the route into [runtime prefetching](https://preview.nextjs.org/docs/app/guides/runtime-prefetching) with [`prefetch = 'allow-runtime'`](https://preview.nextjs.org/docs/app/api-reference/file-conventions/route-segment-config/prefetch#allow-runtime) so the cached content is prefetched behind the runtime read.
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.
```jsx filename="app/dashboard/page.js"
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function getMetrics(range) {
'use cache'
+6 -6
View File
@@ -19,7 +19,7 @@ If you are generating a token to talk to a database that itself should be cached
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function getCachedData(token: string, userId: string) {
"use cache"
@@ -37,7 +37,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function getCachedData(userId: string) {
"use cache"
@@ -59,7 +59,7 @@ If you require this random value to be unique per Request and an async version o
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { generateKeySync } from 'node:crypto'
@@ -75,7 +75,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { generateKey } from 'node:crypto'
@@ -95,7 +95,7 @@ If you require this random value to be unique per Request and an async version o
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ params }) {
const { sessionId } = await params
@@ -107,7 +107,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { connection } from 'next/server'
@@ -23,7 +23,7 @@ If you are using the current time for performance tracking with elapsed time use
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ params }) {
const { id } = await params
@@ -39,7 +39,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ params }) {
const { id } = await params
@@ -61,7 +61,7 @@ If you want to read the time when some cache entry is created (such as when a Ne
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function InformationTable({ id }) {
const data = await fetch(urlFrom(id))
@@ -87,7 +87,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function InformationTable({ id }) {
'use cache'
@@ -124,7 +124,7 @@ If you go with this approach you will need to ensure the Client Component which
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
function RelativeTime({ when }) {
return computeTimeAgo(new Date(), when)
@@ -178,7 +178,7 @@ export function RelativeTime({ when }) {
```
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { RelativeTime } from './relative-time'
@@ -212,7 +212,7 @@ Next.js enforces that it can always produce at least a partially static initial
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
const lastImpressionTime = (await cookies()).get(
@@ -230,7 +230,7 @@ export default async function Page() {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { Suspense } from 'react'
import { connection } from 'next/server'
+4 -4
View File
@@ -23,7 +23,7 @@ If your random value is cacheable, move the `Math.random()` call to a `"use cach
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ params }) {
const { category } = await params
@@ -37,7 +37,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function RandomizedProductsView({ category }) {
'use cache'
@@ -62,7 +62,7 @@ If you want the random value to be evaluated on each Request precede it with `aw
Before:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ params }) {
const { category } = await params
@@ -76,7 +76,7 @@ export default async function Page({ params }) {
After:
```jsx filename="app/page.js"
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
import { connection } from 'next/server'
@@ -41,17 +41,11 @@ const PrefetchSchema = z.enum([
'partial',
'unstable_eager',
'force-disabled',
'allow-runtime',
])
export type Instant = InstantConfig | true | false
export type Prefetch =
| 'auto'
| 'partial'
| 'unstable_eager'
| 'force-disabled'
| 'allow-runtime'
export type Prefetch = 'auto' | 'partial' | 'unstable_eager' | 'force-disabled'
export type InstantConfigForTypeCheckInternal = __GenericInstantConfig | Instant
// the __GenericInstantConfig type is used to avoid type widening issues with
@@ -142,12 +136,11 @@ const AppSegmentConfigSchema = z.object({
* Controls prefetching for this segment.
* - 'auto' (default) is a noop.
* - 'partial' enables Partial Prefetching. Only Cache Components are
* prefetched, not dynamic ones.
* prefetched, not dynamic ones. When a static prefetch is insufficient,
* the segment may be prefetched with a runtime request instead.
* - 'unstable_eager' behaves like 'partial' but, when App Shells are enabled,
* keeps eagerly prefetching the route's segments instead of relying on the
* shared app shell. Internal migration aid; not part of the public API.
* - 'allow-runtime' is a superset of 'partial' and permits prefetching with
* a runtime request instead of a static one.
* - 'force-disabled' disables prefetching for the segment.
*/
prefetch: PrefetchSchema.optional(),
@@ -204,7 +197,7 @@ export function parseAppSegmentConfig(
}
case 'prefetch': {
return {
message: `Invalid prefetch value ${JSON.stringify(ctx.data)} on "${route}", must be "auto", "partial", "unstable_eager", "force-disabled", or "allow-runtime".`,
message: `Invalid prefetch value ${JSON.stringify(ctx.data)} on "${route}", must be "auto", "partial", "unstable_eager", or "force-disabled".`,
}
}
case 'unstable_dynamicStaleTime': {
@@ -270,12 +263,11 @@ export type AppSegmentConfig = {
* Controls prefetching for this segment.
* - 'auto' (default) is a noop.
* - 'partial' enables Partial Prefetching. Only Cache Components are
* prefetched, not dynamic ones.
* prefetched, not dynamic ones. When a static prefetch is insufficient,
* the segment may be prefetched with a runtime request instead.
* - 'unstable_eager' behaves like 'partial' but, when App Shells are enabled,
* keeps eagerly prefetching the route's segments instead of relying on the
* shared app shell. Internal migration aid; not part of the public API.
* - 'allow-runtime' is a superset of 'partial' and permits prefetching with
* a runtime request instead of a static one.
* - 'force-disabled' disables prefetching for the segment.
*/
prefetch?: Prefetch
@@ -296,7 +296,6 @@ import {
type AdvanceableRenderStage,
} from './staged-rendering'
import {
anySegmentHasRuntimePrefetchEnabled,
anySegmentHasPartialPrefetchingEnabled,
isPageAllowedToBlock,
anySegmentNeedsInstantValidationInDev,
@@ -1039,14 +1038,12 @@ async function generateStagedDynamicFlightRenderResultNode(
// Check if this route should runtime-cache its navigation. This happens when
// Partial Prefetching is enabled for the route, either per segment (a
// `prefetch` of 'partial', 'unstable_eager', or 'allow-runtime') or globally
// (the `partialPrefetching` config), or when the `cachedNavigations:
// 'allow-runtime'` flag forces it for every route. If so, we piggyback on the
// dynamic render to fill caches and then spawn a final runtime prerender
// whose result stream is embedded in the RSC payload. This is gated because
// it adds extra server processing and increases the response payload size.
// `prefetch` of 'partial' or 'unstable_eager') or globally (the
// `partialPrefetching` config). If so, we piggyback on the dynamic render to
// fill caches and then spawn a final runtime prerender whose result stream
// is embedded in the RSC payload. This is gated because it adds extra server
// processing and increases the response payload size.
if (
experimental.cachedNavigations === 'allow-runtime' ||
Boolean(renderOpts.partialPrefetching) ||
(await anySegmentHasPartialPrefetchingEnabled(loaderTree))
) {
@@ -1393,7 +1390,7 @@ async function generateDynamicFlightRenderResultWithStagesInDev(
const prefetchMode = await getPrefetchingModeForPage(renderOpts, loaderTree)
// A client navigation into a runtime-prefetch route extends the shell
// A client navigation into a Partial Prefetching route extends the shell
// through the runtime-prefetchable content: it has already settled on the
// client (via the prefetch) by the time it navigates, so it belongs in this
// response's shell. Everything else uses the static shell, like an initial
@@ -1412,9 +1409,7 @@ async function generateDynamicFlightRenderResultWithStagesInDev(
// (which can have more data than the app shell)
prefetchStage = RenderStage.ShellRuntime
} else {
prefetchStage = (await anySegmentHasRuntimePrefetchEnabled(loaderTree))
? RenderStage.Runtime
: RenderStage.Static
prefetchStage = RenderStage.Static
}
}
@@ -3615,11 +3610,9 @@ async function renderToStream(
// embedded in the initial RSC payload so the client can cache
// runtime-prefetchable content during hydration. This is enabled when
// Partial Prefetching is on for the route, either per segment (a
// `prefetch` of 'partial', 'unstable_eager', or 'allow-runtime') or
// globally (the `partialPrefetching` config), or when the
// `cachedNavigations: 'allow-runtime'` flag forces it for every route.
// `prefetch` of 'partial' or 'unstable_eager') or globally (the
// `partialPrefetching` config).
if (
cachedNavigations === 'allow-runtime' ||
Boolean(renderOpts.partialPrefetching) ||
(await anySegmentHasPartialPrefetchingEnabled(tree))
) {
@@ -667,10 +667,11 @@ async function collectPrefetchHintsImpl(
// data, the parent cannot be inlined into this segment because there's no
// static response to carry it. See the ParentInlinedIntoSelf check below.
//
// Runtime-prefetch segments (prefetch: 'allow-runtime') are NOT disabled:
// they have static data — emitted unconditionally, so the client can
// attempt a static prefetch before deciding whether the runtime request is
// needed — and are measured and inlined like any other segment.
// Partial Prefetching segments are NOT disabled even though they may need
// a runtime prefetch: they have static data — emitted unconditionally, so
// the client can attempt a static prefetch before deciding whether the
// runtime request is needed — and are measured and inlined like any other
// segment.
const isStaticPrefetchDisabled =
((route[4] ?? 0) & StaticPrefetchDisabled) !== 0
@@ -820,14 +821,14 @@ async function collectPrefetchHintsImpl(
// static response.
//
// A disabled segment (prefetch: 'force-disabled' / instant = false) is
// never a valid target — it has no response at all. A runtime-prefetch
// segment (prefetch: 'allow-runtime'), by contrast, DOES have a static
// response (see isStaticPrefetchDisabled above) and is an ordinary
// candidate. Runtime prefetching gets no special treatment here: whether
// the client will actually issue a runtime request can't be known at
// build time (a static prefetch attempt may prove sufficient and skip
// it), so the head must always be reachable through the static responses
// — inlined into one of them, or outlined.
// never a valid target — it has no response at all. A Partial Prefetching
// segment, by contrast, DOES have a static response (see
// isStaticPrefetchDisabled above) and is an ordinary candidate. Runtime
// prefetching gets no special treatment here: whether the client will
// actually issue a runtime request can't be known at build time (a static
// prefetch attempt may prove sufficient and skip it), so the head must
// always be reachable through the static responses — inlined into one of
// them, or outlined.
const isBundleTerminal = !didInlineIntoChild && !isStaticPrefetchDisabled
const segment = route[0]
const isPageSegment =
@@ -1104,13 +1105,13 @@ function collectSegmentDataImpl(
// the bundle chain but with null data. The client will skip creating a
// cache entry for it.
//
// Runtime-prefetch segments (prefetch: 'allow-runtime') are NOT disabled:
// their static data is emitted UNCONDITIONALLY — it can't be gated on the
// ShouldAttemptStaticPrefetch hint, because the client walks bundles
// positionally and the null-slot positions must be deterministic from
// build-time config alone. The client uses the data to attempt a static
// prefetch before deciding whether the segment's runtime request is
// actually needed.
// Partial Prefetching segments are NOT disabled even though they may need
// a runtime prefetch: their static data is emitted UNCONDITIONALLY — it
// can't be gated on the ShouldAttemptStaticPrefetch hint, because the
// client walks bundles positionally and the null-slot positions must be
// deterministic from build-time config alone. The client uses the data to
// attempt a static prefetch before deciding whether the segment's runtime
// request is actually needed.
const staticPrefetchDisabled = (prefetchHints & StaticPrefetchDisabled) !== 0
const rsc = seedData !== null && !staticPrefetchDisabled ? seedData[0] : null
@@ -1287,9 +1288,10 @@ async function renderSegmentPrefetch(
while (node !== null) {
const elementRsc = node.rsc
if (elementRsc === null) {
// Static prefetching disabled (prefetch: 'force-disabled'; allow-runtime
// segments carry real data): a null placeholder keeps the array aligned
// with the client's bundle list, which skips a cache entry for the slot.
// Static prefetching disabled (prefetch: 'force-disabled'; Partial
// Prefetching segments carry real data): a null placeholder keeps the
// array aligned with the client's bundle list, which skips a cache
// entry for the slot.
data.push(null)
} else {
// We can determine if a segment contains only partial data if it takes
@@ -122,31 +122,14 @@ async function createFlightRouterStateFromLoaderTreeImpl(
PrefetchHint.SubtreeHasEagerPrefetch
} else if (prefetchConfig === 'force-disabled') {
prefetchHints |= PrefetchHint.PrefetchDisabled
} else if (prefetchConfig === 'allow-runtime') {
// 'allow-runtime' participates in the two-phase (Shell then Speculative)
// prefetch flow, so it counts as Partial Prefetching. HasRuntimePrefetch
// additionally marks the segment as requiring runtime completeness: the
// prefetch isn't done for this segment until an entry at least as
// complete as a runtime response exists. That doesn't disable static
// prefetching — the server emits static data for the segment
// unconditionally, and the scheduler may attempt a static prefetch first
// (per the ShouldAttemptStaticPrefetch hint), issuing the runtime
// request only if the static response proves insufficient.
prefetchHints |=
PrefetchHint.HasRuntimePrefetch |
PrefetchHint.SubtreeHasPartialPrefetching
}
// Mark the segment as "eager" unless its effective prefetch strategy is
// 'partial' or 'allow-runtime'. A truthy instant is treated as
// 'partial' (not eager). 'unstable_eager' already set the bit above. Under
// App Shells, a subtree with no eager segment skips its Speculative prefetch
// and relies on the shared app shell instead.
if (
!isInstant &&
prefetchConfig !== 'partial' &&
prefetchConfig !== 'allow-runtime'
) {
// 'partial'. A truthy instant is treated as 'partial' (not eager).
// 'unstable_eager' already set the bit above. Under App Shells, a subtree
// with no eager segment skips its Speculative prefetch and relies on the
// shared app shell instead.
if (!isInstant && prefetchConfig !== 'partial') {
prefetchHints |= PrefetchHint.SubtreeHasEagerPrefetch
}
@@ -50,36 +50,9 @@ export function isFrameworkErrorRoute(route: string | undefined): boolean {
)
}
export async function anySegmentHasRuntimePrefetchEnabled(
tree: LoaderTree
): Promise<boolean> {
const { mod: layoutOrPageMod } = await getLayoutOrPageModule(tree)
// TODO(restart-on-cache-miss): Does this work correctly for client page/layout modules?
const prefetchConfig = layoutOrPageMod
? (layoutOrPageMod as AppSegmentConfig).prefetch
: undefined
if (prefetchConfig === 'allow-runtime') {
return true
}
const { parallelRoutes } = parseLoaderTree(tree)
for (const parallelRouteKey in parallelRoutes) {
const parallelRoute = parallelRoutes[parallelRouteKey]
const hasChildRuntimePrefetch =
await anySegmentHasRuntimePrefetchEnabled(parallelRoute)
if (hasChildRuntimePrefetch) {
return true
}
}
return false
}
/**
* Like `anySegmentHasRuntimePrefetchEnabled`, but matches any `prefetch` config
* that enables Partial Prefetching for the segment: 'partial',
* 'unstable_eager', or 'allow-runtime'. A route with Partial Prefetching
* Matches any `prefetch` config that enables Partial Prefetching for the
* segment: 'partial' or 'unstable_eager'. A route with Partial Prefetching
* enabled also runtime-caches its navigations, so this gates the runtime
* prefetch spawn.
*/
@@ -88,14 +61,11 @@ export async function anySegmentHasPartialPrefetchingEnabled(
): Promise<boolean> {
const { mod: layoutOrPageMod } = await getLayoutOrPageModule(tree)
// TODO(restart-on-cache-miss): Does this work correctly for client page/layout modules?
const prefetchConfig = layoutOrPageMod
? (layoutOrPageMod as AppSegmentConfig).prefetch
: undefined
if (
prefetchConfig === 'partial' ||
prefetchConfig === 'unstable_eager' ||
prefetchConfig === 'allow-runtime'
) {
if (prefetchConfig === 'partial' || prefetchConfig === 'unstable_eager') {
return true
}
@@ -1077,7 +1077,6 @@ export async function createCombinedPayloadAtDepth(
parallelRoutes[parallelRouteKey],
path,
parallelRouteKey,
false /* isInsideRuntimePrefetch */,
0 /* segmentDepth */
)
slotResults.set(parallelRouteKey, result)
@@ -1167,7 +1166,6 @@ export async function createCombinedPayloadAtDepth(
lt: LoaderTree,
parentPath: SegmentPath | null,
key: string | null,
isInsideRuntimePrefetch: boolean,
segmentDepth: number
): Promise<TreeResult> {
const { parallelRoutes } = parseLoaderTree(lt)
@@ -1182,11 +1180,9 @@ export async function createCombinedPayloadAtDepth(
: createChildSegmentPath(parentPath, key!, segment)
let instantConfig: Instant | null = null
let prefetchConfig: AppSegmentConfig['prefetch'] | null = null
let localCreateInstantStack: (() => Error) | null = null
if (layoutOrPageMod !== undefined) {
instantConfig = (layoutOrPageMod as AppSegmentConfig).instant ?? null
prefetchConfig = (layoutOrPageMod as AppSegmentConfig).prefetch ?? null
// When the default validation level is active and this is a page or
// default segment without an explicit config, treat it as if
@@ -1219,7 +1215,6 @@ export async function createCombinedPayloadAtDepth(
}
let stage: PrefetchedSegmentStage
let childIsInsideRuntimePrefetch = isInsideRuntimePrefetch
switch (prefetchKind) {
case ValidationPrefetchKind.Shell: {
@@ -1228,31 +1223,17 @@ export async function createCombinedPayloadAtDepth(
} else {
stage = RenderStage.ShellRuntime
}
// We do not set or track
// - `[child]isInsideRuntimePrefetch`
// - `has{Static,Runtime}Segments`
// because they do not affect shell prefetches.
// We do not track `has{Static,Runtime}Segments` because they do not
// affect shell prefetches.
break
}
case ValidationPrefetchKind.LegacySpeculative: {
const segmentHasRuntimePrefetch = prefetchConfig === 'allow-runtime'
if (!isInsideRuntimePrefetch) {
if (segmentHasRuntimePrefetch) {
stage = RenderStage.Runtime
childIsInsideRuntimePrefetch = true
} else {
if (useRuntimeStageForPartialSegments) {
stage = RenderStage.Runtime
} else {
// In legacy speculative prefetches, we always use static
// for segments that aren't under an allow-runtime boundary.
// In legacy speculative prefetches, we always use static.
stage = RenderStage.Static
}
}
} else {
stage = RenderStage.Runtime
}
break
}
}
@@ -1297,7 +1278,6 @@ export async function createCombinedPayloadAtDepth(
parallelRoutes[parallelRouteKey],
path,
parallelRouteKey,
childIsInsideRuntimePrefetch,
childSegmentDepth
)
slotResults.set(parallelRouteKey, result)
+1 -1
View File
@@ -169,7 +169,7 @@ export interface RenderOptsPartial {
authInterrupts: boolean
serverComponentsHmrCancellation?: boolean
useCacheTimeout: number
cachedNavigations: boolean | 'allow-runtime'
cachedNavigations: boolean
/**
* The maximum size (in bytes) of the postponed state body for PPR resume
+1 -3
View File
@@ -222,9 +222,7 @@ export const experimentalSchema = {
craCompat: z.boolean().optional(),
caseSensitiveRoutes: z.boolean().optional(),
clientParamParsingOrigins: z.array(z.string()).optional(),
cachedNavigations: z
.union([z.boolean(), z.literal('allow-runtime')])
.optional(),
cachedNavigations: z.boolean().optional(),
dynamicOnHover: z.boolean().optional(),
useOffline: z.boolean().optional(),
optimisticRouting: z.boolean().optional(),
+2 -5
View File
@@ -496,12 +496,9 @@ export interface ExperimentalConfig {
/**
* Caches subsets of a route, seeded from actual navigations, so subsequent
* navigations to the same or similar pages can be served instantly. Requires
* Cache Components. `true` caches the static stage only (the runtime stage is
* opted into per segment via `export const prefetch = 'allow-runtime'`).
* `'allow-runtime'` additionally treats every segment as runtime-cached,
* regardless of its per-segment `prefetch` config.
* Cache Components.
*/
cachedNavigations?: boolean | 'allow-runtime'
cachedNavigations?: boolean
dynamicOnHover?: boolean
useOffline?: boolean
optimisticRouting?: boolean
+1 -4
View File
@@ -2277,10 +2277,7 @@ function enforceExperimentalFeatures(
config.partialPrefetching = true
}
// TODO: Remove this once cachedNavigations is the default. Note:
// cachedNavigations may be the string 'allow-runtime'. These guards treat it
// as truthy, so an explicit 'allow-runtime' is respected here and in the
// cacheComponents-tied default below rather than being downgraded to `true`.
// TODO: Remove this once cachedNavigations is the default.
if (
process.env.__NEXT_EXPERIMENTAL_CACHED_NAVIGATIONS === 'true' &&
// We do respect an explicit value in the user config.
@@ -159,20 +159,19 @@ const API_DOCS: Record<
prefetch: {
description: `Controls prefetching behavior for this segment. Some options are experimental and may change.`,
link: '(docs coming soon)',
type: `"auto" | "partial" | "unstable_eager" | "force-disabled" | "allow-runtime"`,
type: `"auto" | "partial" | "unstable_eager" | "force-disabled"`,
options: {
auto: 'Default. Framework decides based on instant validation and segment configuration. You do not need to set this explicitly.',
partial: 'Enables Partial Prefetching for this segment.',
partial:
'Enables Partial Prefetching for this segment. When a static prefetch is insufficient, Next.js may prefetch the segment with a runtime server request so it can access session data, such as cookies.',
unstable_eager:
'Like "partial", but adds an implied prop of prefetch={true} to ' +
'every Link. This option only exists to aid migration of apps that ' +
'adopted Partial Prefetching in canary before the behavior changed to ' +
'only fetch the shell by default.',
'force-disabled': 'Never prefetch this segment.',
'allow-runtime':
'Allows Next.js to prefetch this segment with a runtime server request so it can access session data, such as cookies.',
},
insertText: `prefetch = 'allow-runtime';`,
insertText: `prefetch = 'partial';`,
},
unstable_dynamicStaleTime: {
description: `Controls how long the client-side router cache retains dynamic page data (in seconds). Pages only — not allowed in layouts. Cannot be combined with \`instant\`.`,
@@ -162,7 +162,7 @@ export type FlightRouterState = [
refresh?: 'refetch' | 'inside-shared-layout' | 'metadata-only' | null,
/**
* Bitmask of PrefetchHint flags. Encodes route structure metadata:
* root layout, loading boundaries, instant configs, and runtime prefetch
* root layout, loading boundaries, instant configs, and prefetch strategy
* hints. Only set when non-zero.
*/
prefetchHints?: number,
@@ -181,22 +181,25 @@ export type FlightRouterState = [
export type CompressedRefreshState = [url: string, renderedSearch: string]
export const enum PrefetchHint {
// This segment has runtime prefetching enabled (prefetch: 'allow-runtime';
// no other config maps to this bit). Marks the segment as requiring
// RUNTIME COMPLETENESS: a prefetch isn't considered done for this segment
// until an entry at least as complete as a runtime response exists. It
// does NOT mean the segment lacks static data — the server emits static
// data for these segments unconditionally, and the scheduler may attempt a
// static prefetch first (per ShouldAttemptStaticPrefetch), issuing the
// runtime request only if the static response's own `needsRuntimeRequest`
// signal says it would return more. Per-segment only, does not propagate
// to ancestors.
HasRuntimePrefetch = 0b00001,
// NOTE: The 0b00001 bit was previously HasRuntimePrefetch (prefetch:
// 'allow-runtime'). Partial Prefetching now implies runtime completeness
// for every segment, so the bit was removed. Do not reuse it without
// considering caches populated by older builds.
// This segment or one of its descendants opts into Partial Prefetching, i.e.
// uses the two-phase (Shell then Speculative) prefetch flow. Set when a
// truthy instant config is present, or `prefetch` is 'partial',
// 'unstable_eager', or 'allow-runtime'. Propagates upward so the root segment
// reflects the entire subtree.
// truthy instant config is present, or `prefetch` is 'partial' or
// 'unstable_eager'. Propagates upward so the root segment reflects the
// entire subtree.
//
// Partial Prefetching segments require RUNTIME COMPLETENESS: a prefetch
// isn't considered done for such a segment until an entry at least as
// complete as a runtime response exists. This does NOT mean the segment
// lacks static data — the server emits static data unconditionally, and the
// scheduler may attempt a static prefetch first (per
// ShouldAttemptStaticPrefetch), issuing the runtime request only if the
// static response's own `needsRuntimeRequest` signal says it would
// return more.
SubtreeHasPartialPrefetching = 0b00010,
// This segment itself has a loading.tsx boundary.
SegmentHasLoadingBoundary = 0b00100,
@@ -237,15 +240,16 @@ export const enum PrefetchHint {
// prefetch data exists" — not a user opt-out. Split the fallback into its
// own bit so the two intents can diverge.
PrefetchDisabled = 0b10000000000,
// This segment or one of its descendants has runtime prefetch enabled
// (HasRuntimePrefetch). Propagates upward so the root reflects the
// entire subtree.
SubtreeHasRuntimePrefetch = 0b100000000000,
// NOTE: The 0b100000000000 bit was previously SubtreeHasRuntimePrefetch.
// Partial Prefetching now implies runtime completeness for every segment
// (see SubtreeHasPartialPrefetching), so the bit was removed. Do not reuse
// it without considering caches populated by older builds.
// This segment or one of its descendants prefetches "eagerly" — i.e. its
// effective prefetch strategy is anything other than 'partial' or
// 'allow-runtime'. Used by App Shells: a non-eager subtree relies on the
// shared app shell and skips its Speculative prefetch. Propagates upward so
// the root reflects the entire subtree.
// effective prefetch strategy is anything other than 'partial'. Used by
// App Shells: a non-eager subtree relies on the shared app shell and skips
// its Speculative prefetch. Propagates upward so the root reflects the
// entire subtree.
SubtreeHasEagerPrefetch = 0b1000000000000,
// This segment or one of its descendants exports `instant = false`,
// explicitly opting out of Partial Prefetching. Propagates upward so the root
@@ -284,15 +288,14 @@ export const enum PrefetchHint {
* the client never issues a static request for it.
*
* Static prefetching is disabled ONLY by `prefetch: 'force-disabled'`
* (PrefetchDisabled). Notably, `prefetch: 'allow-runtime'`
* (HasRuntimePrefetch) segments DO have static data: the server emits it
* unconditionally — it can't be gated on the
* ShouldAttemptStaticPrefetch hint, because null-slot positions in segment
* bundles must be deterministic from build-time config. HasRuntimePrefetch
* instead marks runtime completeness: a runtime request may still be needed
* for the segment, but the scheduler may attempt a static prefetch first
* (per the ShouldAttemptStaticPrefetch hint) and skip the runtime request if
* the static response proves sufficient.
* (PrefetchDisabled). Notably, Partial Prefetching segments DO have static
* data even though they require runtime completeness: the server emits it
* unconditionally — it can't be gated on the ShouldAttemptStaticPrefetch
* hint, because null-slot positions in segment bundles must be deterministic
* from build-time config. A runtime request may still be needed for the
* segment, but the scheduler may attempt a static prefetch first (per the
* ShouldAttemptStaticPrefetch hint) and skip the runtime request if the
* static response proves sufficient.
*
* Usage: `(hints & StaticPrefetchDisabled) !== 0`
*/
@@ -307,15 +310,14 @@ export const StaticPrefetchDisabled = PrefetchHint.PrefetchDisabled
export const SubtreePrefetchHints =
PrefetchHint.SubtreeHasPartialPrefetching |
PrefetchHint.SubtreeHasLoadingBoundary |
PrefetchHint.SubtreeHasRuntimePrefetch |
PrefetchHint.SubtreeHasInstantFalse |
PrefetchHint.SubtreeHasEagerPrefetch
/**
* Folds a child segment's prefetch hints into its parent's, propagating the
* "subtree" flags. A child's segment-local flag (e.g. it has a loading boundary,
* or it has a runtime prefetch) becomes the corresponding "subtree" flag on the
* parent, so the root segment ends up reflecting the entire subtree.
* "subtree" flags. A child's segment-local flag (e.g. it has a loading
* boundary) becomes the corresponding "subtree" flag on the parent, so the
* root segment ends up reflecting the entire subtree.
*
* Used wherever a route tree is assembled bottom-up: on the server when building
* a prefetch tree (createFlightRouterStateFromLoaderTree) and on the client when
@@ -338,13 +340,6 @@ export function propagateSubtreeBits(
) {
parentHints |= PrefetchHint.SubtreeHasLoadingBoundary
}
// Likewise for runtime prefetch.
if (
childHints &
(PrefetchHint.HasRuntimePrefetch | PrefetchHint.SubtreeHasRuntimePrefetch)
) {
parentHints |= PrefetchHint.SubtreeHasRuntimePrefetch
}
// And for eager prefetch. The bit is set directly on each eager segment, so
// there's no separate segment-local flag — propagate it as-is.
if (childHints & PrefetchHint.SubtreeHasEagerPrefetch) {
@@ -4,8 +4,7 @@ import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'
// Note: this is intended for partialPrefetching where
// shells can access session data, so no `export const prefetch = "allow-runtime"
// is necessary.
// shells can access session data.
// If the flag is not enabled globally, opt the page in manually to excercise both codepaths.
export const prefetch = process.env.__NEXT_PARTIAL_PREFETCHING
? 'auto'
@@ -3,7 +3,7 @@ import { cookies } from 'next/headers'
import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function RuntimePrefetchable() {
await cookies()
@@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'
import { cacheLife } from 'next/cache'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
// A distinct slug per test keys a separate cache entry (so the first request
// for each slug is a genuine cold miss), while both tests share this one
@@ -1,7 +1,7 @@
import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function PrivateCached() {
'use cache: private'
@@ -477,7 +477,7 @@ export function runDevWarmupTests({
assertLog(logs, 'after first cache', 'Prerender')
assertLog(logs, 'after cookies', 'Prefetch')
if (hasRuntimePrefetch || partialPrefetching) {
// in partialPrefetching (either via allow-runtime or global flag),
// in partialPrefetching (via per-segment config or global flag),
// sync IO in the runtime stage errors and advances to Server.
assertLog(logs, 'after sync io', 'Server')
assertLog(logs, 'after cache read - page', 'Server')
@@ -4,7 +4,7 @@ import { connection } from 'next/server'
import { Suspense } from 'react'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__PAGE__'
@@ -1,7 +1,7 @@
import { ReactNode, Suspense } from 'react'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
// `lang` is covered by `generateStaticParams`. At this segment `params` only
// contains `lang`, so reading it here (rather than in the deeper page) lets us
@@ -3,7 +3,7 @@ import { UncachedFetch, CachedData } from '../data-fetching'
import { PrivateCachedData } from './data-fetching'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = '/private-cache/__LAYOUT__'
@@ -3,7 +3,7 @@ import { UncachedFetch, CachedData } from '../data-fetching'
import { ShortLivedCache } from './data-fetching'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__LAYOUT__'
@@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { UncachedFetch, CachedData } from '../data-fetching'
import { ShortStaleCache } from './data-fetching'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__LAYOUT__'
@@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { UncachedFetch, CachedFetch, CachedData } from '../data-fetching'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__LAYOUT__'
@@ -1,5 +1,5 @@
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -3,7 +3,7 @@ import { CachedData, getCachedData } from '../../data-fetching'
import { cookies } from 'next/headers'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__PAGE__'
@@ -1,7 +1,7 @@
import { CachedData, getCachedData } from '../../data-fetching'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
const CACHE_KEY = __dirname + '/__PAGE__'
@@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { Static, Runtime, Dynamic } from '../shared'
export const instant = true
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Root({ children }: { children: React.ReactNode }) {
return (
@@ -1,7 +1,7 @@
import { Suspense } from 'react'
import { setTimeout } from 'timers/promises'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
async function getCachedValue(id: string) {
'use cache'
@@ -2,7 +2,7 @@ import { nextTestSetup } from 'e2e-utils'
import { retry, waitFor } from 'next-test-utils'
// Partial prefetching is enabled here (and Cache Components), so a client
// navigation to a route with `export const prefetch = 'allow-runtime'` reveals
// navigation to a route with `export const prefetch = 'partial'` reveals
// the runtime shell. Caches that resolve in the runtime stage (a `'use cache'`
// read after `await params`, or a private cache) are therefore part of the
// shell for that navigation, so a cold cache must show the Cold cache badge,
@@ -1,6 +1,6 @@
import { Suspense } from 'react'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -4,7 +4,7 @@ import Link from 'next/link'
import { ClientFeatures } from './client'
import { HydrationMarker } from '../../hydration-marker'
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
function Box({
label,
@@ -10,7 +10,7 @@ async function getCachedValue() {
'use cache'
// `expire: 0` gives a short, dynamic (client-only) cache life, excluded from
// static prerenders. Reusing it across client navigations would require
// opting the route into runtime prefetching (`prefetch = 'allow-runtime'`) so
// opting the route into Partial Prefetching (`prefetch = 'partial'`) so
// Cached Navigations embeds it into the client router cache; this fixture
// doesn't, since the test only exercises the dev front handler serving it
// warm on reloads.
@@ -13,7 +13,7 @@ export const instant: {
} = {
unstable_samples: [{ params: { lang: 'en', slug: 'anything' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function MixedParamsRuntimePage({
params,
@@ -4,7 +4,7 @@ import { connection } from 'next/server'
export const instant = {
unstable_samples: [{ searchParams: { myParam: 'testValue' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
type SearchParams = { [key: string]: string | string[] | undefined }
@@ -9,7 +9,7 @@ export const instant: {
} = {
unstable_samples: [{ params: { slug: 'anything' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function UngeneratedParamsRuntimePage({
params,
@@ -3,7 +3,7 @@ import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -3,7 +3,7 @@ import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -3,7 +3,7 @@ import { Suspense } from 'react'
import { ensureThrows } from '../../../../ensure-error'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -15,7 +15,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -10,7 +10,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -10,7 +10,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -16,7 +16,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export function generateStaticParams() {
return [{ slug: 'foo' }, { slug: 'bar' }]
@@ -6,7 +6,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ headers: [] }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -6,7 +6,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ headers: [] }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -6,7 +6,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ headers: [] }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -15,7 +15,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -10,7 +10,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -10,7 +10,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -5,7 +5,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ cookies: [{ name: 'auth', value: '1' }] }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
const c = await cookies()
@@ -13,7 +13,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -13,7 +13,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -13,7 +13,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -6,7 +6,7 @@ export const instant: Instant = {
level: 'experimental-error',
unstable_samples: [{ params: { slug: 'hello' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -6,7 +6,7 @@ export const instant: Instant = {
level: 'experimental-error',
unstable_samples: [{ params: { slug: 'hello' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -4,7 +4,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ searchParams: { q: 'test' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -4,7 +4,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ searchParams: { q: 'test' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -12,7 +12,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -11,7 +11,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -16,7 +16,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
type SearchParams = Record<string, string | string[]>
@@ -13,7 +13,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -15,7 +15,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -14,7 +14,7 @@ export const instant: Instant = {
},
],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -2,7 +2,7 @@ import { cookies } from 'next/headers'
import { IgnoreServerContent } from './client'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -1,7 +1,7 @@
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -1,7 +1,7 @@
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Page() {
return (
@@ -1,7 +1,7 @@
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await cachedIO('static')
@@ -1,5 +1,5 @@
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await privateCachedIO()
@@ -1,7 +1,7 @@
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await cookies()
@@ -6,7 +6,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ cookies: [{ name: 'auth', value: '1' }] }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
return (
@@ -8,7 +8,7 @@ export const instant: Instant = {
// no samples
unstable_samples: [{}],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
// Guard behind cookies() so that the rest of this component only runs during validation
@@ -8,7 +8,7 @@ export const instant: Instant = {
// no samples
unstable_samples: [{}],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
// Guard behind cookies() so that the rest of this component only runs during validation
@@ -7,7 +7,7 @@ export const instant: Instant = {
level: 'experimental-error',
unstable_samples: [{ params: { lang: 'en-from-samples' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
// Guard behind cookies() so that the rest of this component only runs during validation
@@ -5,7 +5,7 @@ export const instant: Instant = {
unstable_samples: [{ params: { slug: '123' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -4,7 +4,7 @@ export const instant: Instant = {
level: 'experimental-error',
unstable_samples: [{ searchParams: {} }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({ searchParams }) {
return (
@@ -2,7 +2,7 @@ export const instant = {
unstable_samples: [{ cookies: [] }],
unstable_disableValidation: true,
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function Layout({ children }) {
return <>{children}</>
@@ -4,7 +4,7 @@ export const instant = {
unstable_samples: [{ cookies: [] }],
unstable_disableValidation: true,
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await connection()
@@ -4,7 +4,7 @@ export const instant = {
unstable_samples: [{ cookies: [] }],
unstable_disableValidation: true,
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await connection()
@@ -4,7 +4,7 @@ import { connection } from 'next/server'
import { Suspense } from 'react'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
// Note that we're inside a root layout with suspense, so we skip the static shell
export async function generateViewport(): Promise<Viewport> {
@@ -1,5 +1,5 @@
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Layout({ children }) {
return <>{children}</>
@@ -4,7 +4,7 @@ import { connection } from 'next/server'
import { Suspense } from 'react'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export async function generateMetadata(): Promise<Metadata> {
await connection()
@@ -2,7 +2,7 @@ import type { Viewport } from 'next'
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export async function generateViewport(): Promise<Viewport> {
await cookies()
@@ -2,7 +2,7 @@ import { Suspense } from 'react'
import { connection } from 'next/server'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default function SlotPage() {
return (
@@ -1,7 +1,7 @@
import { cookies } from 'next/headers'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function RuntimeLayout({ children }) {
await cookies()
@@ -5,7 +5,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ cookies: [], params: { param: '123' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
params,
@@ -16,8 +16,8 @@ export default async function Page({
<main>
<div>
<p>
Params need a suspense boundary even with "allow-runtime", because we
need a valid App Shell
Params need a suspense boundary even on a runtime-prefetchable route,
because we need a valid App Shell
</p>
<LinkData params={params} />
</div>
@@ -5,7 +5,7 @@ export const instant = {
level: 'experimental-error',
unstable_samples: [{ cookies: [], searchParams: { foo: 'bar' } }],
}
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page({
searchParams,
@@ -16,8 +16,8 @@ export default async function Page({
<main>
<div>
<p>
Search Params need a suspense boundary even with allow-runtime because
we need a valid App Shell
Search Params need a suspense boundary even on a runtime-prefetchable
route because we need a valid App Shell
</p>
<LinkData searchParams={searchParams} />
</div>
@@ -2,7 +2,7 @@ import { connection } from 'next/server'
import { ReactNode } from 'react'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Layout({ children }: { children: ReactNode }) {
await connection()
@@ -2,7 +2,7 @@ import { cookies } from 'next/headers'
import { connection } from 'next/server'
export const instant = { level: 'experimental-error' }
export const prefetch = 'allow-runtime'
export const prefetch = 'partial'
export default async function Page() {
await cookies()

Some files were not shown because too many files have changed in this diff Show More