Legacy streaming vs non-streaming server APIs
Streaming APIs (renderToReadableStream, renderToPipeableStream, resume, resumeToPipeableStream) are available in environments that support Web Streams or Node.js Streams. Legacy non-streaming APIs (renderToString, renderToStaticMarkup) can be used in environments that don't support streams but have limited functionality.
resume API for Web Streams
resume resumes prerender to a Readable Web Stream. This method is available in environments with Web Streams, which includes browsers, Deno, and some modern edge runtimes.
renderToPipeableStream API
renderToPipeableStream renders a React tree to a pipeable Node.js Stream. This method is only available in environments with Node.js Streams.
renderToString API
renderToString renders a React tree to a string. This method can be used in environments that don't support streams and has limited functionality compared to the streaming APIs.
renderToStaticMarkup API
renderToStaticMarkup renders a non-interactive React tree to a string. This method can be used in environments that don't support streams and has limited functionality compared to the streaming APIs.
react-dom/server APIs overview
The react-dom/server APIs let you server-side render React components to HTML. These APIs are only used on the server at the top level of your app to generate the initial HTML. A framework may call them for you. Most components don't need to import or use them.
renderToReadableStream API
renderToReadableStream renders a React tree to a Readable Web Stream. This method is available in environments with Web Streams, which includes browsers, Deno, and some modern edge runtimes.
resumeToPipeableStream API
resumeToPipeableStream resumes prerenderToNodeStream to a pipeable Node.js Stream. This method is only available in environments with Node.js Streams.
Web Streams vs Node.js Streams APIs performance note
Node.js includes Web Streams methods (renderToReadableStream and resume) for compatibility, but they are not recommended due to worse performance. Use the dedicated Node.js APIs (renderToPipeableStream and resumeToPipeableStream) instead.
resumeToPipeableStream option caveats
resumeToPipeableStream does not accept options for bootstrapScripts, bootstrapScriptContent, or bootstrapModules—these must be passed to the prerender call instead. It does not accept identifierPrefix since the prefix must be the same in both prerender and resumeToPipeableStream. nonce should only be provided if you're not providing scripts to prerender. resumeToPipeableStream re-renders from the root until it finds a component that was not fully pre-rendered; only fully prerendered components are skipped entirely.
resumeToPipeableStream signature
The signature of resumeToPipeableStream is: const {pipe, abort} = await resumeToPipeableStream(reactNode, postponedState, options?). It returns an object with two methods: pipe and abort.
resumeToPipeableStream purpose and Node.js specific usage
resumeToPipeableStream streams a pre-rendered React tree to a pipeable Node.js Stream. It is specific to Node.js. Environments with Web Streams, like Deno and modern edge runtimes, should use resume (renderToReadableStream) instead.
resumeToPipeableStream parameters
resumeToPipeableStream accepts three parameters: reactNode (the React node, expected to represent the entire document with an <html> tag), postponedState (the opaque postpone object returned from a prerender API), and optional options object. The options object can contain: nonce (string for Content-Security-Policy script-src), onAllReady (callback when all rendering is complete), onBrowserBailout (callback when recovering from browser() by leaving a Suspense fallback), onError (callback for server errors), onShellReady (callback after shell finishes), and onShellError (callback if there was an error rendering the shell).
resumeToPipeableStream return value methods
resumeToPipeableStream returns an object with two methods: pipe (outputs HTML into the provided Writable Node.js Stream, call in onShellReady to enable streaming or in onAllReady for crawlers and static generation) and abort (allows aborting server rendering and rendering the rest on the client).
resumeToPipeableStream what it is used for
resumeToPipeableStream is used to resume rendering a pre-rendered React tree as HTML into a Node.js Stream. It accepts the postponedState from a previous prerender call and continues rendering.
resume caveats
resume does not accept options for bootstrapScripts, bootstrapScriptContent, or bootstrapModules - these must be passed to the prerender call instead. resume does not accept identifierPrefix since the prefix must be the same in both prerender and resume. nonce should only be provided to resume if not providing scripts to prerender. resume re-renders from the root until it finds a component that was not fully pre-rendered; only fully prerendered components are skipped entirely.
resume signature and parameters
The resume function signature is: const stream = await resume(reactNode, postponedState, options?). The reactNode parameter is the React node to render (typically <App />), expected to render the entire document with an <html> tag. The postponedState parameter is the opaque postpone object returned from a prerender API call. The options parameter is optional and is an object with streaming options.
resume options object
The resume function accepts optional options with the following properties: nonce (string, optional) - a nonce value to allow scripts for script-src Content-Security-Policy; signal (AbortSignal, optional) - an abort signal to abort server rendering and render the rest on the client; onBrowserBailout (callback, optional, canary) - called when React recovers from browser() by leaving a Suspense fallback for the browser to replace, receives an Error and errorInfo object with componentStack; onError (callback, optional) - called whenever there is a server error (recoverable or not), defaults to console.error.
resume return value
resume returns a Promise. If resume successfully produces a shell, the Promise resolves to a Readable Web Stream that can be piped to a Writable Web Stream. If an error happens in the shell, the Promise rejects with that error. The returned stream has an additional property: allReady, a Promise that resolves when all rendering is complete, which can be awaited before returning a response for crawlers and static generation.
resume usage and dependencies
resume is used for resuming a pre-rendered React tree as HTML into a Readable Web Stream. It requires a postponedState object from a prerender API call. resume depends on Web Streams API; for Node.js environments, use resumeToNodeStream instead. The function is imported from 'react-dom/server'.
Custom error handling with instanceof checks
You can create custom Error subclasses and use instanceof to check which error type was thrown. This lets your onError, onShellReady, and onShellError callbacks handle different errors differently, such as returning different status codes for NotFoundError vs other errors.
Waiting for all content with onAllReady for crawlers
For crawlers or static generation, use onAllReady instead of onShellReady to wait for all content to load before streaming. Check an isCrawler flag: in onShellReady, only pipe if !isCrawler; in onAllReady, only pipe if isCrawler. This gives crawlers the final HTML but regular visitors get progressive loading.
Aborting server rendering with abort
You can call abort() on the returned object to force server rendering to give up after a timeout. React will flush the remaining loading fallbacks as HTML and attempt to render the rest on the client. Example: const { pipe, abort } = renderToPipeableStream(<App />, {...}); setTimeout(() => { abort(); }, 10000);
Streaming more content with Suspense boundaries
Wrap components with <Suspense> boundaries to enable streaming. React will send the HTML for the loading fallback first, and then send the remaining HTML along with an inline <script> tag that replaces the loading fallback once the content finishes loading. This allows users to see content progressively.
renderToPipeableStream signature and return value
renderToPipeableStream renders a React tree to a pipeable Node.js Stream. The signature is: const { pipe, abort } = renderToPipeableStream(reactNode, options?). It returns an object with two methods: pipe, which outputs HTML into a provided Writable Node.js Stream, and abort, which lets you abort server rendering and render the rest on the client.
renderToPipeableStream is Node.js specific
renderToPipeableStream is specific to Node.js environments. Environments with Web Streams, like Deno and modern edge runtimes, should use renderToReadableStream instead.
renderToPipeableStream reactNode parameter
The reactNode parameter is a React node you want to render to HTML, such as a JSX element like <App />. It is expected to represent the entire document, so the App component should render the <html> tag.
renderToPipeableStream options: onAllReady
onAllReady is an optional callback that fires when all rendering is complete, including both the shell and all additional content. You can use this instead of onShellReady for crawlers and static generation. If you start streaming here, you won't get any progressive loading and the stream will contain the final HTML.
renderToPipeableStream options: onShellReady
onShellReady is an optional callback that fires right after the initial shell has been rendered. You can set the status code and call pipe here to start streaming. React will stream the additional content after the shell along with the inline <script> tags that replace the HTML loading fallbacks with the content.
renderToPipeableStream options: onShellError
onShellError is an optional callback that fires if there was an error rendering the initial shell. It receives the error as an argument. No bytes were emitted from the stream yet, and neither onShellReady nor onAllReady will get called, so you can output a fallback HTML shell.
renderToPipeableStream options: progressiveChunkSize
progressiveChunkSize is an optional number specifying the number of bytes in a chunk. This follows a default heuristic for determining chunk sizes.
renderToPipeableStream example with bootstrapScripts
import { renderToPipeableStream } from 'react-dom/server';
app.use('/', (request, response) => {
const { pipe } = renderToPipeableStream(<App />, {
bootstrapScripts: ['/main.js'],
onShellReady() {
response.setHeader('content-type', 'text/html');
pipe(response);
}
});
});
Server-rendered HTML structure with doctype and scripts
React will inject the doctype and bootstrap <script> tags into the resulting HTML stream. The output will be: <!DOCTYPE html><html><!-- ... HTML from your components ... --></html><script src="/main.js" async=""></script>
Asset map pattern for hashed asset URLs
To handle hashed asset filenames from the build process, your root component can read the real filenames from a map passed as a prop. Pass the assetMap from the server to the component, and serialize it via bootstrapScriptContent so the client can access it via window.assetMap. Both client and server must render App with the same assetMap prop to avoid hydration errors.
Nested Suspense boundaries for granular streaming
You can nest <Suspense> boundaries to create a more granular loading sequence. Only components not wrapped in any <Suspense> boundary must finish rendering first. Components inside nested boundaries will stream their loading fallbacks and content as they load.
Streaming does not wait for React or JavaScript to load
HTML content from the server will get progressively revealed before any of the <script> tags load. This means users can see content before React itself loads in the browser or the app becomes interactive.
Shell definition in server rendering
The shell is the part of your app outside of any <Suspense> boundaries. It determines the earliest loading state that the user may see. The shell should feel minimal but complete, like a skeleton of the entire page layout.
onShellReady callback timing
The onShellReady callback fires when the entire shell has been rendered. By the time onShellReady fires, components in nested <Suspense> boundaries might still be loading data. Usually, you'll start streaming then by calling pipe.
Error logging pattern with custom onError
To log crash reports, override the onError callback to call both console.error and your logging function. Example: onError(error) { console.error(error); logServerCrashReport(error); }
onShellError fallback HTML pattern
If an error occurs while rendering the shell, override onShellError to send a fallback HTML. At this point no bytes were emitted from the stream yet. Example: onShellError(error) { response.statusCode = 500; response.setHeader('content-type', 'text/html'); response.send('<h1>Something went wrong</h1>'); }
Error recovery outside the shell
If an error happens in a component wrapped in <Suspense> (outside the shell), React will emit the loading fallback into the HTML, give up on server rendering that content, and retry rendering it on the client. If client retry also fails, React throws the error on the client. The onError and client onRecoverableError callbacks will fire.
Status code tradeoff with streaming
Once you start streaming, you can no longer set the response status code. Use the shell to solve this: if the shell errors, use onShellError to set an error status code. Otherwise, send OK status in onShellReady.
Track errors outside shell for status code
You can use a flag like didError that gets set in onError to track whether any error occurred outside the shell. Then in onShellReady, set response.statusCode = didError ? 500 : 200. This only catches errors that happened while generating the initial shell content.
renderToReadableStream signature and return type
renderToReadableStream has the signature: const stream = await renderToReadableStream(reactNode, options?). It returns a Promise that resolves to a Readable Web Stream if rendering the shell is successful, or rejects if rendering the shell fails. The returned stream has an additional property allReady, a Promise that resolves when all rendering is complete, including both the shell and all additional content.
renderToReadableStream parameters: reactNode and options
renderToReadableStream takes two parameters: (1) reactNode - a React node to render to HTML, expected to represent the entire document including the <html> tag; (2) options - an optional object with streaming options.
renderToReadableStream options: bootstrapScriptContent
bootstrapScriptContent is an optional string option for renderToReadableStream that, if specified, will be placed in an inline <script> tag.
renderToReadableStream options: bootstrapScripts
bootstrapScripts is an optional array of string URLs for <script> tags to emit on the page. Use this to include the <script> that calls hydrateRoot. Omit it if you don't want to run React on the client at all.
renderToReadableStream options: bootstrapModules
bootstrapModules is an optional option for renderToReadableStream, similar to bootstrapScripts, but emits <script type="module"> tags instead.
renderToReadableStream options: formState
formState is an optional option for renderToReadableStream containing the form state from a form submission handled by a Server Function. If the page is rendered in response to a submission of a form that uses useActionState with a permalink, pass the resulting form state so that React embeds it into the HTML for hydration. The same value must be passed to hydrateRoot on the client.
renderToReadableStream options: identifierPrefix
identifierPrefix is an optional string option for renderToReadableStream that React uses for IDs generated by useId. Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as passed to hydrateRoot.
renderToReadableStream options: importMap
importMap is an optional object option for renderToReadableStream with imports and scopes properties. React emits it as an inline <script type="importmap"> tag before any module scripts, so that <script type="module"> tags can use bare module specifiers. When nonce is set, it is also applied to the import map script.
renderToReadableStream options: maxHeadersLength
maxHeadersLength is an optional number option for renderToReadableStream specifying the maximum total length of the header content passed to onHeaders, measured in UTF-16 code units. Defaults to 2000. Once the limit is reached, React stops adding resource hints to the headers.
renderToReadableStream options: namespaceURI
namespaceURI is an optional string option for renderToReadableStream with the root namespace URI for the stream. Defaults to regular HTML. Pass 'http://www.w3.org/2000/svg' for SVG or 'http://www.w3.org/1998/Math/MathML' for MathML.
renderToReadableStream options: nonce
nonce is an optional string option for renderToReadableStream containing a nonce value to allow scripts for script-src Content-Security-Policy. To use different nonces for scripts and styles, pass an object with script and style properties instead.
renderToReadableStream options: onBrowserBailout
onBrowserBailout is an optional callback option for renderToReadableStream that React calls when it recovers from browser() by leaving a Suspense fallback for the browser to replace. It receives an Error describing the browser-only render and an errorInfo object containing the componentStack. If a reason was passed to browser, it is available as error.cause. By default, React does nothing.
renderToReadableStream options: onError
onError is an optional callback option for renderToReadableStream that fires whenever there is a server error, whether recoverable or not. By default, this only calls console.error. If you override it to log crash reports, make sure that you still call console.error. You can also use it to adjust the status code before the shell is emitted.
renderToReadableStream options: onHeaders
onHeaders is an optional callback option for renderToReadableStream that fires when React has determined the resource hints for the document, such as preconnects and stylesheet, font, or high-priority image preloads. It receives a Headers instance containing the corresponding Link header value, so you can send it as an HTTP response header or as a 103 Early Hints response. React calls it even when there are no resource hints to send. The header content is capped by maxHeadersLength.
renderToReadableStream options: progressiveChunkSize
progressiveChunkSize is an optional number option for renderToReadableStream specifying the number of bytes in a chunk. The default heuristic can be read about in the React source code.
renderToReadableStream options: signal
signal is an optional AbortSignal option for renderToReadableStream that lets you abort server rendering and render the rest on the client.
renderToReadableStream basic usage example
import { renderToReadableStream } from 'react-dom/server';
async function handler(request) {
const stream = await renderToReadableStream(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(stream, {
headers: { 'content-type': 'text/html' },
});
}