Cached Navigations: Cache static stage of partially static initial HTML#90539
Merged
unstubbable merged 1 commit intocanaryfrom Mar 4, 2026
Merged
Cached Navigations: Cache static stage of partially static initial HTML#90539unstubbable merged 1 commit intocanaryfrom
unstubbable merged 1 commit intocanaryfrom
Conversation
Contributor
Author
This was referenced Feb 25, 2026
Collaborator
Tests Passed |
59984fa to
c0d147a
Compare
d37c0ed to
1040d61
Compare
c0d147a to
963e5dc
Compare
Collaborator
Stats from current PR🔴 2 regressions
📊 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 | 771 B | 766 B | ✓ |
| Total | 771 B | 766 B | ✅ -5 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 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 | 256 B | 254 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 | 59.8 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 | 256 kB | ✓ |
| Total | 379 kB | 380 kB |
Middleware
| Canary | PR | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 617 B | 617 B | ✓ |
| middleware-r..fest.js gzip | 156 B | 155 B | ✓ |
| middleware.js gzip | 43.5 kB | 43.9 kB | ✓ |
| edge-runtime..pack.js gzip | 842 B | 842 B | ✓ |
| Total | 45.2 kB | 45.5 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.05 MB | 4.06 MB | 🔴 +6.84 kB (+0%) |
| index.pack gzip | 103 kB | 103 kB | ✓ |
| index.pack.old gzip | 104 kB | 103 kB | ✓ |
| Total | 4.26 MB | 4.26 MB |
🔄 Shared (bundler-independent)
Runtimes
| Canary | PR | Change | |
|---|---|---|---|
| app-page-exp...dev.js gzip | 321 kB | 321 kB | ✓ |
| app-page-exp..prod.js gzip | 170 kB | 170 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 | 168 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.8 kB | 70.8 kB | ✓ |
| app-route-ex..prod.js gzip | 49.3 kB | 49.3 kB | ✓ |
| app-route-tu...dev.js gzip | 70.9 kB | 70.9 kB | ✓ |
| app-route-tu..prod.js gzip | 49.3 kB | 49.3 kB | ✓ |
| app-route-tu...dev.js gzip | 70.5 kB | 70.5 kB | ✓ |
| app-route-tu..prod.js gzip | 49 kB | 49 kB | ✓ |
| app-route.ru...dev.js gzip | 70.4 kB | 70.4 kB | ✓ |
| app-route.ru..prod.js gzip | 49 kB | 49 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
Diff too large to display
app-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/77ae56e3cf6ffa38a24eace9a471c684b7184603/next
963e5dc to
20180b4
Compare
1040d61 to
2898e04
Compare
2898e04 to
ef7164a
Compare
20180b4 to
b83417d
Compare
b83417d to
c50db36
Compare
d1641a9 to
c1baae9
Compare
ffe57f0 to
d56a60a
Compare
c1baae9 to
36bfd79
Compare
d56a60a to
7cbc8ac
Compare
36bfd79 to
11785d9
Compare
4808cf1 to
cfc45eb
Compare
11785d9 to
2f969e0
Compare
acdlite
approved these changes
Mar 4, 2026
2f969e0 to
d414c69
Compare
When a partially static page is loaded via initial HTML (PPR resume), the RSC payload now includes the `l` (static stage byte length) field, enabling the client to extract and cache the static stage during hydration. Subsequent navigations to the same page serve the cached static content instantly while streaming in dynamic content. On the server, the resume path in `renderToStream` now uses staged rendering (mirroring `generateStagedDynamicFlightRenderResult`) when Cache Components is enabled. A `StagedRenderingController` separates the static and dynamic stages, and `countStaticStageBytes` resolves the byte length promise that Flight serializes into the stream as `l`. `getRSCPayload` accepts `staleTimeIterable` and `staticStageByteLengthPromise` options to wire `s` and `l` into the `InitialRSCPayload`. On the client, `app-index.tsx` tees the inlined Flight stream when Cache Components is enabled. One copy goes to React for decoding, the other is passed to `createInitialRouterState`. When `l` is present (partially static), the clone is truncated at the byte boundary via the new `decodeStaticStage` helper and written into the segment cache via `processStaticStageResponse` and `writeStaticStageResponseIntoCache`. When `l` is absent but `s` is present (fully static), the existing `writeInitialSeedDataIntoCache` path is used unchanged. `createInitialRouterState` now accepts a single `initialRSCPayload: InitialRSCPayload` prop instead of individual destructured fields. `resolveStaticStageData`, `processStaticStageResponse`, `writeStaticStageResponseIntoCache`, and `writeDynamicRenderResponseIntoCache` are widened to accept `NavigationFlightResponse | InitialRSCPayload`.
d414c69 to
77ae56e
Compare
sokra
pushed a commit
that referenced
this pull request
Mar 6, 2026
…ML (#90539) When a partially static page is loaded via initial HTML (PPR resume), the RSC payload now includes the `l` (static stage byte length) field, enabling the client router to extract and cache the static stage during hydration. Subsequent navigations to the same page serve the cached static content instantly while streaming in dynamic content. On the server, the resume path in `renderToStream` now uses staged rendering (mirroring `generateStagedDynamicFlightRenderResult`) when Cache Components is enabled. A `StagedRenderingController` separates the static and dynamic stages, and `countStaticStageBytes` resolves the byte length promise that Flight serializes into the stream as `l`. `getRSCPayload` accepts `staleTimeIterable` and `staticStageByteLengthPromise` options to wire `s` and `l` into the `InitialRSCPayload`. On the client, `app-index.tsx` tees the inlined Flight stream when Cache Components is enabled. One copy goes to React for decoding, the other is passed to `createInitialRouterState`. When `l` is present (partially static), the clone is truncated at the byte boundary via `decodeStaticStage` and written into the segment cache via `writeStaticStageResponseIntoCache` with `isResponsePartial: true`. When `l` is absent but `s` is present (fully static), the same function is used with `isResponsePartial: false`. `createInitialRouterState` now accepts a single `initialRSCPayload: InitialRSCPayload` prop instead of individual destructured fields.
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 a partially static page is loaded via initial HTML (PPR resume), the RSC payload now includes the
l(static stage byte length) field, enabling the client router to extract and cache the static stage during hydration. Subsequent navigations to the same page serve the cached static content instantly while streaming in dynamic content.On the server, the resume path in
renderToStreamnow uses staged rendering (mirroringgenerateStagedDynamicFlightRenderResult) when Cache Components is enabled. AStagedRenderingControllerseparates the static and dynamic stages, andcountStaticStageBytesresolves the byte length promise that Flight serializes into the stream asl.getRSCPayloadacceptsstaleTimeIterableandstaticStageByteLengthPromiseoptions to wiresandlinto theInitialRSCPayload.On the client,
app-index.tsxtees the inlined Flight stream when Cache Components is enabled. One copy goes to React for decoding, the other is passed tocreateInitialRouterState. Whenlis present (partially static), the clone is truncated at the byte boundary viadecodeStaticStageand written into the segment cache viawriteStaticStageResponseIntoCachewithisResponsePartial: true. Whenlis absent butsis present (fully static), the same function is used withisResponsePartial: false.createInitialRouterStatenow accepts a singleinitialRSCPayload: InitialRSCPayloadprop instead of individual destructured fields.