mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
In #95064 we added a global opt-in to the runtime stage of Cached Navigations: setting `cachedNavigations: 'allow-runtime'` makes every route runtime-cache, on top of the existing per-segment `prefetch = 'allow-runtime'`. This extends the opt-in: a `prefetch` config of `'partial'`, `'unstable_eager'`, or `'allow-runtime'` all enable Partial Prefetching for a route, and a route already doing PPR-style partial prefetching should also runtime-cache its navigations. The next-config `partialPrefetching`, which enables Partial Prefetching globally, opts every route in as well. Both runtime-prefetch spawn gates in `app-render.tsx` now broaden their condition to include `partialPrefetching` and any segment `prefetch` that enables Partial Prefetching, via a new `anySegmentHasPartialPrefetchingEnabled` helper. The narrower callers that gate metadata staging and the dev reveal stage keep using `anySegmentHasRuntimePrefetchEnabled`, since they specifically concern routes the client runtime-prefetches (`'allow-runtime'`), not partial ones. Because `partialPrefetching` has no default and is not auto-enabled by `cacheComponents`, apps that do not set it are unaffected. Tests add `prefetch = 'partial'` and `prefetch = 'unstable_eager'` routes to the existing `default` fixture and a new `partial-prefetching` fixture for the global config, each asserting the route runtime-caches its request-derived content on a second navigation without a per-segment `prefetch = 'allow-runtime'` export.
71 lines
1.8 KiB
TypeScript
71 lines
1.8 KiB
TypeScript
import { cookies, headers } from 'next/headers'
|
|
import { connection } from 'next/server'
|
|
import { Suspense } from 'react'
|
|
|
|
// Note: intentionally no `export const prefetch` and no `instant` config. This
|
|
// route gets runtime-cached purely because of the global `partialPrefetching`
|
|
// config in next.config.ts.
|
|
|
|
export default async function Page({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ q?: string }>
|
|
}) {
|
|
return (
|
|
<main>
|
|
<CachedContent />
|
|
<div id="search-params-boundary">
|
|
<Suspense fallback={<p>Loading search params...</p>}>
|
|
<SearchParamsContent searchParams={searchParams} />
|
|
</Suspense>
|
|
</div>
|
|
<div id="cookies-boundary">
|
|
<Suspense fallback={<p>Loading cookies...</p>}>
|
|
<CookiesContent />
|
|
</Suspense>
|
|
</div>
|
|
<div id="headers-boundary">
|
|
<Suspense fallback={<p>Loading headers...</p>}>
|
|
<HeadersContent />
|
|
</Suspense>
|
|
</div>
|
|
<div id="connection-boundary">
|
|
<Suspense fallback={<p>Loading connection...</p>}>
|
|
<ConnectionContent />
|
|
</Suspense>
|
|
</div>
|
|
</main>
|
|
)
|
|
}
|
|
|
|
async function CachedContent() {
|
|
'use cache'
|
|
return <p id="cached-content">Cached content</p>
|
|
}
|
|
|
|
async function SearchParamsContent({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ q?: string }>
|
|
}) {
|
|
const { q } = await searchParams
|
|
return <p>Search params: {q ?? 'none'}</p>
|
|
}
|
|
|
|
async function CookiesContent() {
|
|
const cookieStore = await cookies()
|
|
const value = cookieStore.get('testCookie')?.value ?? 'none'
|
|
return <p>Cookie: {value}</p>
|
|
}
|
|
|
|
async function HeadersContent() {
|
|
const headerStore = await headers()
|
|
const value = headerStore.get('x-test-header') ?? 'none'
|
|
return <p>Header: {value}</p>
|
|
}
|
|
|
|
async function ConnectionContent() {
|
|
await connection()
|
|
return <p>Dynamic content</p>
|
|
}
|