Cached Navigations: Cache runtime stage data from navigation requests#90666
Merged
unstubbable merged 2 commits intocanaryfrom Mar 4, 2026
Merged
Cached Navigations: Cache runtime stage data from navigation requests#90666unstubbable merged 2 commits intocanaryfrom
unstubbable merged 2 commits intocanaryfrom
Conversation
This was referenced Feb 27, 2026
Contributor
Author
Collaborator
Stats from current PR🔴 1 regression
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles: **401 kB** → **401 kB**
|
| Canary | PR | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 767 B | 766 B | ✓ |
| Total | 767 B | 766 B | ✅ -1 B |
Build Details
Build Manifests
| Canary | PR | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 450 B | 452 B | ✓ |
| Total | 450 B | 452 B |
📦 Webpack
Client
Main Bundles
| Canary | PR | Change | |
|---|---|---|---|
| 5528-HASH.js gzip | 5.54 kB | N/A | - |
| 6280-HASH.js gzip | 59.1 kB | N/A | - |
| 6335.HASH.js gzip | 169 B | N/A | - |
| 912-HASH.js gzip | 4.59 kB | N/A | - |
| e8aec2e4-HASH.js gzip | 62.6 kB | N/A | - |
| framework-HASH.js gzip | 59.7 kB | 59.7 kB | ✓ |
| main-app-HASH.js gzip | 255 B | 253 B | ✓ |
| main-HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
| webpack-HASH.js gzip | 1.68 kB | 1.68 kB | ✓ |
| 262-HASH.js gzip | N/A | 4.59 kB | - |
| 2889.HASH.js gzip | N/A | 169 B | - |
| 5602-HASH.js gzip | N/A | 5.55 kB | - |
| 6948ada0-HASH.js gzip | N/A | 62.6 kB | - |
| 9544-HASH.js gzip | N/A | 60.2 kB | - |
| Total | 233 kB | 234 kB |
Polyfills
| Canary | PR | Change | |
|---|---|---|---|
| polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
| Total | 39.4 kB | 39.4 kB | ✓ |
Pages
| Canary | PR | Change | |
|---|---|---|---|
| _app-HASH.js gzip | 194 B | 194 B | ✓ |
| _error-HASH.js gzip | 183 B | 180 B | 🟢 3 B (-2%) |
| css-HASH.js gzip | 331 B | 330 B | ✓ |
| dynamic-HASH.js gzip | 1.81 kB | 1.81 kB | ✓ |
| edge-ssr-HASH.js gzip | 256 B | 256 B | ✓ |
| head-HASH.js gzip | 351 B | 352 B | ✓ |
| hooks-HASH.js gzip | 384 B | 383 B | ✓ |
| image-HASH.js gzip | 580 B | 581 B | ✓ |
| index-HASH.js gzip | 260 B | 260 B | ✓ |
| link-HASH.js gzip | 2.51 kB | 2.51 kB | ✓ |
| routerDirect..HASH.js gzip | 320 B | 319 B | ✓ |
| script-HASH.js gzip | 386 B | 386 B | ✓ |
| withRouter-HASH.js gzip | 315 B | 315 B | ✓ |
| 1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
| Total | 7.98 kB | 7.98 kB | ✅ -1 B |
Server
Edge SSR
| Canary | PR | Change | |
|---|---|---|---|
| edge-ssr.js gzip | 125 kB | 125 kB | ✓ |
| page.js gzip | 254 kB | 255 kB | ✓ |
| Total | 379 kB | 380 kB |
Middleware
| Canary | PR | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 618 B | 615 B | ✓ |
| middleware-r..fest.js gzip | 156 B | 155 B | ✓ |
| middleware.js gzip | 43.6 kB | 43.6 kB | ✓ |
| edge-runtime..pack.js gzip | 842 B | 842 B | ✓ |
| Total | 45.2 kB | 45.3 kB |
Build Details
Build Manifests
| Canary | PR | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 715 B | 718 B | ✓ |
| Total | 715 B | 718 B |
Build Cache
| Canary | PR | Change | |
|---|---|---|---|
| 0.pack gzip | 4.04 MB | 4.05 MB | 🔴 +9.57 kB (+0%) |
| index.pack gzip | 103 kB | 103 kB | ✓ |
| index.pack.old gzip | 103 kB | 102 kB | ✓ |
| Total | 4.25 MB | 4.26 MB |
🔄 Shared (bundler-independent)
Runtimes
| Canary | PR | Change | |
|---|---|---|---|
| app-page-exp...dev.js gzip | 321 kB | 322 kB | ✓ |
| app-page-exp..prod.js gzip | 170 kB | 171 kB | ✓ |
| app-page-tur...dev.js gzip | 321 kB | 321 kB | ✓ |
| app-page-tur..prod.js gzip | 170 kB | 170 kB | ✓ |
| app-page-tur...dev.js gzip | 317 kB | 318 kB | ✓ |
| app-page-tur..prod.js gzip | 168 kB | 169 kB | ✓ |
| app-page.run...dev.js gzip | 318 kB | 318 kB | ✓ |
| app-page.run..prod.js gzip | 168 kB | 169 kB | ✓ |
| app-route-ex...dev.js gzip | 70.5 kB | 70.5 kB | ✓ |
| app-route-ex..prod.js gzip | 48.8 kB | 48.8 kB | ✓ |
| app-route-tu...dev.js gzip | 70.6 kB | 70.6 kB | ✓ |
| app-route-tu..prod.js gzip | 48.9 kB | 48.9 kB | ✓ |
| app-route-tu...dev.js gzip | 70.1 kB | 70.1 kB | ✓ |
| app-route-tu..prod.js gzip | 48.6 kB | 48.6 kB | ✓ |
| app-route.ru...dev.js gzip | 70.1 kB | 70.1 kB | ✓ |
| app-route.ru..prod.js gzip | 48.6 kB | 48.6 kB | ✓ |
| dist_client_...dev.js gzip | 324 B | 324 B | ✓ |
| dist_client_...dev.js gzip | 326 B | 326 B | ✓ |
| dist_client_...dev.js gzip | 318 B | 318 B | ✓ |
| dist_client_...dev.js gzip | 317 B | 317 B | ✓ |
| pages-api-tu...dev.js gzip | 43.2 kB | 43.2 kB | ✓ |
| pages-api-tu..prod.js gzip | 32.9 kB | 32.9 kB | ✓ |
| pages-api.ru...dev.js gzip | 43.2 kB | 43.2 kB | ✓ |
| pages-api.ru..prod.js gzip | 32.9 kB | 32.9 kB | ✓ |
| pages-turbo....dev.js gzip | 52.6 kB | 52.6 kB | ✓ |
| pages-turbo...prod.js gzip | 38.5 kB | 38.5 kB | ✓ |
| pages.runtim...dev.js gzip | 52.6 kB | 52.6 kB | ✓ |
| pages.runtim..prod.js gzip | 38.5 kB | 38.5 kB | ✓ |
| server.runti..prod.js gzip | 61.9 kB | 61.9 kB | ✓ |
| Total | 2.83 MB | 2.83 MB |
📝 Changed Files (8 files)
Files with changes:
app-page-exp..ntime.dev.jsapp-page-exp..time.prod.jsapp-page-tur..ntime.dev.jsapp-page-tur..time.prod.jsapp-page-tur..ntime.dev.jsapp-page-tur..time.prod.jsapp-page.runtime.dev.jsapp-page.runtime.prod.js
View diffs
app-page-exp..ntime.dev.js
failed to diffapp-page-exp..time.prod.js
failed to diffapp-page-tur..ntime.dev.js
failed to diffapp-page-tur..time.prod.js
Diff too large to display
app-page-tur..ntime.dev.js
failed to diffapp-page-tur..time.prod.js
failed to diffapp-page.runtime.dev.js
failed to diffapp-page.runtime.prod.js
Diff too large to display
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/f6b0988aa84e6db1d4651ac0982381e22183a2be/next
ef7164a to
9636bd5
Compare
fd712f3 to
4dbe047
Compare
Collaborator
Tests Passed |
9636bd5 to
c6f61a6
Compare
64fe9e0 to
79f2ba5
Compare
8fde131 to
d1641a9
Compare
8190feb to
9431026
Compare
d1641a9 to
c1baae9
Compare
113ee89 to
4639f6d
Compare
c1baae9 to
36bfd79
Compare
4639f6d to
b3ee0c6
Compare
36bfd79 to
11785d9
Compare
b3ee0c6 to
c657498
Compare
11785d9 to
2f969e0
Compare
acdlite
approved these changes
Mar 4, 2026
2f969e0 to
d414c69
Compare
b0b971e to
b309762
Compare
d414c69 to
77ae56e
Compare
b309762 to
02c605e
Compare
When navigating to a page with `unstable_instant: { prefetch: 'runtime'
}` and no prior prefetch (e.g. `prefetch={false}` or clicking before
prefetch completes), the server now embeds a runtime prefetch stream in
the dynamic RSC navigation payload. On the client, this stream is
decoded and written into the segment cache so that subsequent
navigations to the same page can serve runtime-prefetchable content
(cookies, headers, searchParams) instantly, without needing a separate
prefetch request.
On the server, `generateStagedDynamicFlightRenderResult` detects routes
with runtime prefetching enabled and attaches a `CacheSignal` and
`PrerenderResumeDataCache` to the `RequestStore`. The dynamic render
fills these caches as a side effect. Once `cacheSignal.cacheReady()`
resolves, a final runtime prerender (the same 5-stage pipeline used for
regular runtime prefetches) runs and its output is piped into a
`TransformStream` whose readable side is included in the RSC payload as
the `p` field on the `NavigationFlightResponse`. This avoids a separate
prospective prerender because the dynamic render has already warmed the
caches.
On the client, `processRuntimePrefetchStream` strips the completeness
marker, decodes the inner Flight stream, and the result is written into
the segment cache via `writeDynamicRenderResponseIntoCache` with
`FetchStrategy.PPRRuntime`. Both the `navigateToUnknownRoute` and
`fetchMissingDynamicData` code paths handle the new stream.
This is gated on the explicit `unstable_instant` opt-in because it adds
extra server processing (a full runtime prerender per navigation
request) and increases payload size. It also requires that the runtime
prefetch output has been validated first.
Known limitation: the runtime prefetch cache write currently evicts the
static cache entry (which may have a longer stale time) because
`upsertSegmentEntry` treats the fallback match as a replacement target.
This means that after the runtime cache expires, the static cache is no
longer available as a fallback. A follow-up will address this with a
layered cache approach where entries with different fetch strategies and
stale times coexist independently.
Extracting runtime data from initial HTML loads (the PPR resume path) is
also deferred to a follow-up.
77ae56e to
5a18a71
Compare
02c605e to
6a91c94
Compare
6a91c94 to
f6b0988
Compare
sokra
pushed a commit
that referenced
this pull request
Mar 6, 2026
…#90666) When navigating to a page with `unstable_instant: { prefetch: 'runtime' }` and no prior prefetch (e.g. `prefetch={false}` or clicking before prefetch completes), the server now embeds a runtime prefetch stream in the dynamic RSC navigation payload. On the client, this stream is decoded and written into the segment cache so that subsequent navigations to the same page can serve runtime-prefetchable content (cookies, headers, searchParams) instantly, without needing a separate prefetch request. On the server, `generateStagedDynamicFlightRenderResult` detects routes with runtime prefetching enabled and attaches a `CacheSignal` and `PrerenderResumeDataCache` to the `RequestStore`. The dynamic render fills these caches as a side effect. Once `cacheSignal.cacheReady()` resolves, a final runtime prerender (the same 5-stage pipeline used for regular runtime prefetches) runs and its output is piped into a `TransformStream` whose readable side is included in the RSC payload as the `p` field on the `NavigationFlightResponse`. This avoids a separate prospective prerender because the dynamic render has already warmed the caches. On the client, `processRuntimePrefetchStream` strips the isPartial byte, decodes the inner Flight stream, and the result is written into the segment cache via `writeDynamicRenderResponseIntoCache` with `FetchStrategy.PPRRuntime`. Both the `navigateToUnknownRoute` and `fetchMissingDynamicData` code paths handle the new stream. This is gated on the explicit `unstable_instant` opt-in because it adds extra server processing (a full runtime prerender per navigation request) and increases payload size. It also requires that the runtime prefetch output has been validated first. Known limitation: the runtime prefetch cache write currently evicts the static cache entry (which may have a longer stale time) because `upsertSegmentEntry` treats the fallback match as a replacement target. This means that after the runtime cache expires, the static cache is no longer available as a fallback. A follow-up may address this with a layered cache approach where entries with different fetch strategies and stale times coexist independently. Extracting runtime data from initial HTML loads (the PPR resume path) is also deferred to a follow-up.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

When navigating to a page with
unstable_instant: { prefetch: 'runtime' }and no prior prefetch (e.g.prefetch={false}or clicking before prefetch completes), the server now embeds a runtime prefetch stream in the dynamic RSC navigation payload. On the client, this stream is decoded and written into the segment cache so that subsequent navigations to the same page can serve runtime-prefetchable content (cookies, headers, searchParams) instantly, without needing a separate prefetch request.On the server,
generateStagedDynamicFlightRenderResultdetects routes with runtime prefetching enabled and attaches aCacheSignalandPrerenderResumeDataCacheto theRequestStore. The dynamic render fills these caches as a side effect. OncecacheSignal.cacheReady()resolves, a final runtime prerender (the same 5-stage pipeline used for regular runtime prefetches) runs and its output is piped into aTransformStreamwhose readable side is included in the RSC payload as thepfield on theNavigationFlightResponse. This avoids a separate prospective prerender because the dynamic render has already warmed the caches.On the client,
processRuntimePrefetchStreamstrips the isPartial byte, decodes the inner Flight stream, and the result is written into the segment cache viawriteDynamicRenderResponseIntoCachewithFetchStrategy.PPRRuntime. Both thenavigateToUnknownRouteandfetchMissingDynamicDatacode paths handle the new stream.This is gated on the explicit
unstable_instantopt-in because it adds extra server processing (a full runtime prerender per navigation request) and increases payload size. It also requires that the runtime prefetch output has been validated first.Known limitation: the runtime prefetch cache write currently evicts the static cache entry (which may have a longer stale time) because
upsertSegmentEntrytreats the fallback match as a replacement target. This means that after the runtime cache expires, the static cache is no longer available as a fallback. A follow-up may address this with a layered cache approach where entries with different fetch strategies and stale times coexist independently.Extracting runtime data from initial HTML loads (the PPR resume path) is also deferred to a follow-up.