renderToPipeableStream – React
renderToPipeableStream – React
v19
renderToPipeableStream
renderToPipeableStream renders a React tree to a pipeable
Node.js Stream.
Reference
renderToPipeableStream(reactNode, options?)
Usage
Note
https://react.dev/reference/react-dom/server/renderToPipeableStream 1/18
20/02/2025, 19:15 renderToPipeableStream – React
renderToReadableStream instead.
Reference
renderToPipeableStream(reactNode, options?)
Parameters
https://react.dev/reference/react-dom/server/renderToPipeableStream 2/18
20/02/2025, 19:15 renderToPipeableStream – React
optional namespaceURI : A string 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.
https://react.dev/reference/react-dom/server/renderToPipeableStream 3/18
20/02/2025, 19:15 renderToPipeableStream – React
Returns
pipe outputs the HTML into the provided Writable Node.js Stream. Call
pipe in onShellReady if you want to enable streaming, or in onAllReady
for crawlers and static generation.
abort lets you abort server rendering and render the rest on the client.
Usage
Along with the root component , you need to provide a list of bootstrap
<script> paths . Your root component should return the entire document
https://react.dev/reference/react-dom/server/renderToPipeableStream 4/18
20/02/2025, 19:15 renderToPipeableStream – React
React will inject the doctype and your bootstrap <script> tags into the
resulting HTML stream:
<!DOCTYPE html>
<html>
<!-- ... HTML from your components ... -->
</html>
<script src=" /main.js " async=""></script>
On the client, your bootstrap script should hydrate the entire document with
a call to hydrateRoot :
This will attach event listeners to the server-generated HTML and make it
interactive.
https://react.dev/reference/react-dom/server/renderToPipeableStream 5/18
20/02/2025, 19:15 renderToPipeableStream – React
DEEP DIVE
Show Details
Streaming allows the user to start seeing the content even before all the data
has loaded on the server. For example, consider a profile page that shows a
cover, a sidebar with friends and photos, and a list of posts:
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Posts />
</ProfileLayout>
);
}
Imagine that loading data for <Posts /> takes some time. Ideally, you’d want
to show the rest of the profile page content to the user without waiting for
the posts. To do this, wrap Posts in a <Suspense> boundary:
function ProfilePage() {
return (
<ProfileLayout>
https://react.dev/reference/react-dom/server/renderToPipeableStream 6/18
20/02/2025, 19:15 renderToPipeableStream – React
<ProfileCover />
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
This tells React to start streaming the HTML before Posts loads its data.
React will send the HTML for the loading fallback ( PostsGlimmer ) first, and
then, when Posts finishes loading its data, React will send the remaining
HTML along with an inline <script> tag that replaces the loading fallback
with that HTML. From the user’s perspective, the page will first appear with
the PostsGlimmer , later replaced by the Posts .
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<BigSpinner />}>
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</Suspense>
</ProfileLayout>
);
}
https://react.dev/reference/react-dom/server/renderToPipeableStream 7/18
20/02/2025, 19:15 renderToPipeableStream – React
In this example, React can start streaming the page even earlier. Only
ProfileLayout and ProfileCover must finish rendering first because they
Streaming does not need to wait for React itself to load in the browser, or for
your app to become interactive. The HTML content from the server will get
progressively revealed before any of the <script> tags load.
Note
The exact way you would load data in the Posts component above
depends on your framework. If you use a Suspense-enabled
framework, you’ll find the details in its data fetching documentation.
https://react.dev/reference/react-dom/server/renderToPipeableStream 8/18
20/02/2025, 19:15 renderToPipeableStream – React
The part of your app outside of any <Suspense> boundaries is called the
shell:
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<BigSpinner />}>
<Sidebar>
<Friends />
<Photos />
</Sidebar>
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</Suspense>
</ProfileLayout>
);
}
It determines the earliest loading state that the user may see:
<ProfileLayout>
<ProfileCover />
<BigSpinner />
</ProfileLayout>
If you wrap the whole app into a <Suspense> boundary at the root, the shell
will only contain that spinner. However, that’s not a pleasant user experience
because seeing a big spinner on the screen can feel slower and more
annoying than waiting a bit more and seeing the real layout. This is why
usually you’ll want to place the <Suspense> boundaries so that the shell feels
minimal but complete—like a skeleton of the entire page layout.
https://react.dev/reference/react-dom/server/renderToPipeableStream 9/18
20/02/2025, 19:15 renderToPipeableStream – React
The onShellReady callback fires when the entire shell has been rendered.
Usually, you’ll start streaming then:
By default, all errors on the server are logged to console. You can override
this behavior to log crash reports:
https://react.dev/reference/react-dom/server/renderToPipeableStream 10/18
20/02/2025, 19:15 renderToPipeableStream – React
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
If an error occurs while rendering those components, React won’t have any
meaningful HTML to send to the client. Override onShellError to send a
fallback HTML that doesn’t rely on server rendering as the last resort:
https://react.dev/reference/react-dom/server/renderToPipeableStream 11/18
20/02/2025, 19:15 renderToPipeableStream – React
does not have to be an error page. Instead, you may include an alternative
shell that renders your app on the client only.
function ProfilePage() {
return (
<ProfileLayout>
<ProfileCover />
<Suspense fallback={<PostsGlimmer />}>
<Posts />
</Suspense>
</ProfileLayout>
);
}
1. It will emit the loading fallback for the closest <Suspense> boundary
( PostsGlimmer ) into the HTML.
2. It will “give up” on trying to render the Posts content on the server
anymore.
3. When the JavaScript code loads on the client, React will retry rendering
Posts on the client.
If retrying rendering Posts on the client also fails, React will throw the error
on the client. As with all the errors thrown during rendering, the closest
parent error boundary determines how to present the error to the user. In
https://react.dev/reference/react-dom/server/renderToPipeableStream 12/18
20/02/2025, 19:15 renderToPipeableStream – React
practice, this means that the user will see a loading indicator until it is certain
that the error is not recoverable.
If retrying rendering Posts on the client succeeds, the loading fallback from
the server will be replaced with the client rendering output. The user will not
know that there was a server error. However, the server onError callback
and the client onRecoverableError callbacks will fire so that you can get
notified about the error.
By dividing your app into the shell (above all <Suspense> boundaries) and the
rest of the content, you’ve already solved a part of this problem. If the shell
errors, you’ll get the onShellError callback which lets you set the error
status code. Otherwise, you know that the app may recover on the client, so
you can send “OK”.
https://react.dev/reference/react-dom/server/renderToPipeableStream 13/18
20/02/2025, 19:15 renderToPipeableStream – React
});
However, if you’d like, you can use the fact that something has errored to set
the status code:
This will only catch errors outside the shell that happened while generating
the initial shell content, so it’s not exhaustive. If knowing whether an error
occurred for some content is critical, you can move it up into the shell.
You can create your own Error subclasses and use the instanceof operator
to check which error is thrown. For example, you can define a custom
NotFoundError and throw it from your component. Then your onError ,
function getStatusCode() {
if (didError) {
if (caughtError instanceof NotFoundError) {
return 404;
} else {
return 500;
}
} else {
return 200;
}
}
https://react.dev/reference/react-dom/server/renderToPipeableStream 15/18
20/02/2025, 19:15 renderToPipeableStream – React
Keep in mind that once you emit the shell and start streaming, you can’t
change the status code.
Streaming offers a better user experience because the user can see the
content as it becomes available.
However, when a crawler visits your page, or if you’re generating the pages at
the build time, you might want to let all of the content load first and then
produce the final HTML output instead of revealing it progressively.
You can wait for all the content to load using the onAllReady callback:
onError(error) {
didError = true;
console.error(error);
logServerCrashReport(error);
}
});
You can force the server rendering to “give up” after a timeout:
setTimeout(() => {
abort();
}, 10000);
React will flush the remaining loading fallbacks as HTML, and will attempt to
render the rest on the client.
PREVIOUS
Server APIs
https://react.dev/reference/react-dom/server/renderToPipeableStream 17/18
20/02/2025, 19:15 renderToPipeableStream – React
NEXT
renderToReadableStream
uwu?
Describing the UI
Adding Interactivity
Managing State
Escape Hatches
Community More
Acknowledgements Terms
https://react.dev/reference/react-dom/server/renderToPipeableStream 18/18