From b4e0befdb4d0afebe6d89b0ebf485e83bd4e95c2 Mon Sep 17 00:00:00 2001 From: Gael Bost Date: Thu, 5 Mar 2026 02:05:57 +0100 Subject: [PATCH] fix(ssr): preload client reference modules before first SSR render On the first request after server start, client reference modules ("use client" components) are loaded lazily via async import(). When handleSsr calls renderToReadableStream, React SSR tries to render the HTML shell synchronously but can't resolve client components that haven't been imported yet. Since there is no boundary wrapping the root, renderToReadableStream rejects with an undefined error, causing a 500 on every first request after restart. Fix: eagerly preload all client reference modules at the start of handleSsr by calling __vite_rsc_client_require__ for each ID in the virtual:vite-rsc/client-references map. The existing memoize cache in setRequireModule ensures this is only async on the very first call; all subsequent requests resolve from cache immediately. --- packages/vinext/src/server/app-dev-server.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/vinext/src/server/app-dev-server.ts b/packages/vinext/src/server/app-dev-server.ts index 8ea2eac5..5acb2557 100644 --- a/packages/vinext/src/server/app-dev-server.ts +++ b/packages/vinext/src/server/app-dev-server.ts @@ -2296,6 +2296,7 @@ import { setNavigationContext, ServerInsertedHTMLContext } from "next/navigation import { runWithNavigationContext as _runWithNavCtx } from "vinext/navigation-state"; import { safeJsonStringify } from "vinext/html"; import { createElement as _ssrCE } from "react"; +import * as _clientRefs from "virtual:vite-rsc/client-references"; /** * Collect all chunks from a ReadableStream into an array of text strings. @@ -2420,6 +2421,21 @@ function createRscEmbedTransform(embedStream) { * and the data needs to be passed to SSR since they're separate module instances. */ export async function handleSsr(rscStream, navContext, fontData) { + // Eagerly preload all client reference modules before SSR rendering. + // On the first request after server start, client component modules are + // loaded lazily via async import(). Without this preload, React's + // renderToReadableStream rejects because the shell can't resolve client + // components synchronously (there is no Suspense boundary wrapping the + // root). The memoized require cache ensures this is only async on the + // very first call; subsequent requests resolve from cache immediately. + if (_clientRefs?.default && globalThis.__vite_rsc_client_require__) { + await Promise.all( + Object.keys(_clientRefs.default).map((id) => + globalThis.__vite_rsc_client_require__(id).catch?.(() => {}) + ) + ); + } + // Wrap in a navigation ALS scope for per-request isolation in the SSR // environment. The SSR environment has separate module instances from RSC, // so it needs its own ALS scope.