Upstream change
Next.js re-landed a change to renderToNodeFizzStream (Node runtime) that adds a third options argument with a waitForAllReady flag. When set, the pipeable React stream is connected to the output PassThrough only after onAllReady fires, instead of on onShellReady. This effectively gives the prerender path its own forkpoint: the static prerender can wait for the full tree before emitting bytes, while normal SSR keeps streaming as soon as the shell is ready.
The web variant (stream-ops.web.ts) accepts the same option for signature parity but does not change behavior yet.
Relevant diff (packages/next/src/server/app-render/stream-ops.node.ts):
export async function renderToNodeFizzStream(
element: React.ReactElement,
streamOptions: any,
options?: { waitForAllReady?: boolean }
): Promise<FizzStreamResult> {
const pt = new PassThrough()
const shellReady = new DetachedPromise<void>()
const allReady = new DetachedPromise<void>()
const deferPipe = options?.waitForAllReady === true
const pipeable = getTracer().trace(AppRenderSpan.renderToReadableStream, () =>
renderToPipeableStream(element, {
...streamOptions,
onShellReady() {
streamOptions?.onShellReady?.()
if (!deferPipe) {
pipeable.pipe(pt)
}
shellReady.resolve()
},
onAllReady() {
streamOptions?.onAllReady?.()
if (deferPipe) {
pipeable.pipe(pt)
}
allReady.resolve()
},
...
})
)
}
app-render.tsx is also updated to thread this option through prerenderToStream so static prerenders can opt into the deferred pipe.
Why this matters for vinext
vinext targets Cloudflare Workers, so the web renderer (renderToReadableStream) is the primary path and this change is signature-only there. However:
- The Node dev server and any non-Workers prod target use the Node Fizz path. If/when vinext exposes a static prerender or
generateStaticParams build step that needs the full tree before flushing, we should mirror this waitForAllReady forkpoint instead of relying on onShellReady.
- For parity we should make sure our prerender path on the web runtime correctly waits for
allReady when producing static HTML for Cloudflare (e.g. ISR cache writes), since the static output should not be a shell + suspended stream.
This is mainly a tracking item: confirm vinext's prerender code path equivalents pipe only after allReady for static output, both on the Node dev path and the web/Workers prerender path used for ISR.
Suggested work
- Audit vinext's static prerender / ISR generation code paths (App Router) and ensure they wait for the full React tree before serializing HTML to the cache.
- If we ever add a Node-runtime production server, port the
waitForAllReady option to the equivalent stream helper.
- Add a regression test that an ISR-cached page does not contain Suspense boundary fallback markers when the underlying data resolves before the cache write.
Upstream change
Next.js re-landed a change to
renderToNodeFizzStream(Node runtime) that adds a thirdoptionsargument with awaitForAllReadyflag. When set, the pipeable React stream is connected to the outputPassThroughonly afteronAllReadyfires, instead of ononShellReady. This effectively gives the prerender path its own forkpoint: the static prerender can wait for the full tree before emitting bytes, while normal SSR keeps streaming as soon as the shell is ready.The web variant (
stream-ops.web.ts) accepts the same option for signature parity but does not change behavior yet.Relevant diff (
packages/next/src/server/app-render/stream-ops.node.ts):app-render.tsxis also updated to thread this option throughprerenderToStreamso static prerenders can opt into the deferred pipe.Why this matters for vinext
vinext targets Cloudflare Workers, so the web renderer (
renderToReadableStream) is the primary path and this change is signature-only there. However:generateStaticParamsbuild step that needs the full tree before flushing, we should mirror thiswaitForAllReadyforkpoint instead of relying ononShellReady.allReadywhen producing static HTML for Cloudflare (e.g. ISR cache writes), since the static output should not be a shell + suspended stream.This is mainly a tracking item: confirm vinext's prerender code path equivalents pipe only after
allReadyfor static output, both on the Node dev path and the web/Workers prerender path used for ISR.Suggested work
waitForAllReadyoption to the equivalent stream helper.