import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr' import React from 'react' import type { ReactFormState } from 'react-dom/client' import { renderToReadableStream } from 'react-dom/server.edge' import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' export async function renderHTML( rscStream: ReadableStream, options: { formState?: ReactFormState nonce?: string debugNojs?: boolean }, ): Promise<{ stream: ReadableStream; status?: number }> { // duplicate one RSC stream into two. // - one for SSR (ReactClient.createFromReadableStream below) // - another for browser hydration payload by injecting . const [rscStream1, rscStream2] = rscStream.tee() // deserialize RSC stream back to React VDOM let payload: Promise | undefined function SsrRoot() { // deserialization needs to be kicked off inside ReactDOMServer context // for ReactDomServer preinit/preloading to work payload ??= createFromReadableStream(rscStream1) return React.use(payload).root } // render html (traditional SSR) const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent('index') let htmlStream: ReadableStream let status: number | undefined try { htmlStream = await renderToReadableStream(, { bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, nonce: options?.nonce, formState: options?.formState, }) } catch (e) { // fallback to render an empty shell and run pure CSR on browser, // which can replay server component error and trigger error boundary. status = 500 htmlStream = await renderToReadableStream( , { bootstrapScriptContent: `self.__NO_HYDRATE=1;` + (options?.debugNojs ? '' : bootstrapScriptContent), nonce: options?.nonce, }, ) } let responseStream: ReadableStream = htmlStream if (!options?.debugNojs) { // initial RSC stream is injected in HTML stream as // using utility made by devongovett https://github.com/devongovett/rsc-html-stream responseStream = responseStream.pipeThrough( injectRSCPayload(rscStream2, { nonce: options?.nonce, }), ) } return { stream: responseStream, status } }