Skip to content

Add waitForAllReady forkpoint to renderToNodeFizzStream for prerenderToStream #1011

@github-actions

Description

@github-actions

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    nextjs-trackingTracking issue for a Next.js canary change relevant to vinext

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions