mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #1068] [MERGED] perf(pages): cache ISR misses from streamed render #1065
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#1065
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/1068
Author: @NathanDrake2406
Created: 5/5/2026
Status: ✅ Merged
Merged: 5/5/2026
Merged by: @james-elicx
Base:
main← Head:nathan/pages-isr-single-render📝 Commits (3)
4bfa8afperf(pages): cache ISR misses from streamed render09cf2fdfix(pages): keep ISR cache error reporting non-fatal46094f4chore: rerun CI📊 Changes
4 files changed (+196 additions, -40 deletions)
View changed files
📝
packages/vinext/src/entries/pages-server-entry.ts(+0 -1)📝
packages/vinext/src/server/pages-page-response.ts(+109 -33)📝
tests/pages-page-response.test.ts(+86 -5)📝
tests/pages-router.test.ts(+1 -1)📄 Description
What this changes
Pages Router ISR cache misses now cache the same body stream used for the live HTML response instead of rendering the page a second time just to build cache HTML.
The response path keeps the existing render-before-head-collection order, tees the completed body stream when ISR is eligible, sends one branch to the client, and records the other branch into the Pages ISR cache. Nonce-bearing responses still skip ISR writes.
Why
renderPagesPageResponsepreviously rendered the page once for the live response, then created a second page element and calledrenderIsrPassToStringAsync()to generate cached HTML. That second pass can repeat userland render work, data reads, Suspense work, and render-scoped state mutations on every Pages ISR MISS.Next.js Pages ISR stores the generated
RenderResultfrom the response cache path rather than constructing a second React tree for the cache fill. Vinext can do better for Workers by preserving streaming to the client while recording the same streamed body for persistence.Approach
next/head, styled-jsx, and server-inserted HTML state are populated before shell assembly.shellPrefix + body + shellSuffixto ISR cache.renderIsrPassToStringAsyncdependency from the Pages response helper call.Validation
vp check packages/vinext/src/server/pages-page-response.ts packages/vinext/src/entries/pages-server-entry.ts tests/pages-page-response.test.ts tests/pages-router.test.tsvp test run tests/pages-page-response.test.ts tests/pages-page-data.test.tsvp test run tests/pages-router.test.ts -t "caches the streamed ISR render|Pages Router production fixture"vp test run tests/features.test.ts -t "ISR \\(Pages Router\\)"vp run vinext#buildThe package build exits 0 and still emits the existing unresolved virtual-module warnings for virtual RSC/instrumentation imports.
Risks / follow-ups
This follows the existing App Router pattern of scheduling cache writes through
ExecutionContext.waitUntil()when available. An immediate second request after a MISS can only hit once the cache backend has completed the write, which matches the asynchronous cache-write direction already used for App Router HTML caching.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.