Stream SSR HTML from a request handler
Edge or Web Streams server rendering
React 19+ · Function
Renders a complete React tree as HTML into a Web ReadableStream, intended for streaming server rendering in Web Streams environments. The returned promise resolves when the shell is ready.
The function asynchronously renders a React tree as HTML for Web Streams environments. Once the shell is ready, its promise resolves with a ReadableStream that can be placed in a Response; the stream also exposes allReady for waiting until every Suspense-dependent part has finished.
const stream = await renderToReadableStream(reactNode, options?)| Name | Type | Description |
|---|---|---|
reactNode | ReactNode | The React node to render as HTML; it should represent the entire document, including the html element. |
options | object | Optional object containing streaming options such as bootstrapScripts, signal, namespaceURI, and progressiveChunkSize. |
A Promise resolving to a Readable Web Stream when the shell renders successfully; the promise rejects if shell rendering fails. The stream has an allReady Promise for completion of all content.
import { renderToReadableStream } from 'react-dom/server';
function App() {
return <html><body><h1>Hello</h1></body></html>;
}
export default async function handler() {
const stream = await renderToReadableStream(<App />, {
bootstrapScripts: ['/main.js'],
});
return new Response(stream, {
headers: { 'content-type': 'text/html' },
});
}Renders the complete document into a Web ReadableStream and returns it as an HTTP response.
Edge or Web Streams server rendering
Suspense-based server rendering
Crawlers and static generation
Client hydration
It returns a Promise because the shell may need asynchronous rendering before a stream can be provided. A successful shell resolves to a Readable Web Stream; a shell failure rejects the promise.
Follow-up: How would you serve crawlers? Await the returned stream’s allReady promise before returning the response, which sends final HTML instead of progressively revealed content.
Pass the complete React document as reactNode and optionally provide an options object containing streaming configuration. The root component should produce the html element and the result can be used as the body of a Response.
Follow-up: How do you make the generated document interactive? Supply bootstrap scripts and hydrate the server HTML on the client with hydrateRoot.
The API is intended for runtimes implementing Web Streams, such as modern edge environments. For Node.js, the documentation directs you to renderToPipeableStream instead.
Follow-up: What trade-off does allReady introduce? It allows complete output suitable for crawlers or static generation, but removes progressive loading.
renderToPipeableStreamhydrateRootSuspenseallReady