renderToPipeableStream
renderToPipeableStream renders a React tree to a Node.js Writable stream. This is the recommended API for server-side rendering in Node.js environments with full support for Suspense, streaming, and selective hydration.
This is the modern replacement for
renderToString in Node.js. It supports streaming, Suspense, and provides better performance and user experience.Reference
renderToPipeableStream(element, options?)
Renders a React element to a pipeable stream with progressive HTML streaming.
Parameters
element:ReactNode- The React element to renderoptions:Object(optional)identifierPrefix:string- Prefix for IDs generated byuseIdnamespaceURI:string- Namespace URI for the document (e.g., SVG)nonce:string | { script?: string, style?: string }- Nonce for Content Security PolicybootstrapScriptContent:string- Inline script content to run before other scriptsbootstrapScripts:Array<string | BootstrapScriptDescriptor>- External scripts to loadbootstrapModules:Array<string | BootstrapScriptDescriptor>- External ES modules to loadprogressiveChunkSize:number- Target size of progressive chunksonShellReady:() => void- Called when the shell (initial UI) is readyonShellError:(error: mixed) => void- Called if the shell fails to renderonAllReady:() => void- Called when all content, including Suspense boundaries, is readyonError:(error: mixed, errorInfo: ErrorInfo) => ?string- Error handler for rendering errorsunstable_externalRuntimeSrc:string | BootstrapScriptDescriptor- External React runtime scriptimportMap:ImportMap- Import map for module scriptsformState:ReactFormState | null- Form state for progressive enhancementonHeaders:(headers: HeadersDescriptor) => void- Callback for early hintsmaxHeadersLength:number- Maximum length for early hints headers
Returns
Returns an object with two methods:pipe(destination): Pipes the HTML stream to a Node.js Writable stream (likehttp.ServerResponse)abort(reason): Aborts the render and puts remaining content into client-rendered mode
Caveats
Usage
Basic streaming in Express
Streaming with Suspense
onShellReady vs onAllReady
Choose when to start streaming based on your use case:- onShellReady (Users)
- onAllReady (Crawlers)
Error handling
Complete HTML document
Render a complete HTML document with proper structure:Using bootstrap scripts and modules
Setting CSP nonce
Advanced Patterns
Implementing timeouts
Custom HTML streaming wrapper
Implementing early hints (HTTP 103)
Resuming from postponed state
Comparison with Other APIs
- renderToPipeableStream
- renderToString
- renderToReadableStream
- Streaming support
- Suspense support
- Progressive rendering
- Better performance
- Non-blocking
Common Issues
Error: Can only pipe to one writable stream
Error: Can only pipe to one writable stream
pipe() can only be called once per render:Headers already sent error
Headers already sent error
Don’t call
pipe() before setting response headers:Shell never completes (hangs)
Shell never completes (hangs)
If a component outside Suspense boundaries throws a promise, rendering hangs:
Memory leaks with many concurrent renders
Memory leaks with many concurrent renders
Each render keeps state in memory until complete:
Performance Best Practices
Use onShellReady for users
Use onShellReady for users
Stream immediately to minimize time to first byte:
Set appropriate chunk sizes
Set appropriate chunk sizes
Control streaming granularity:
Implement proper timeouts
Implement proper timeouts
Prevent hanging renders:
Use Suspense strategically
Use Suspense strategically
Place boundaries around slow content:
See Also
renderToReadableStream- Web Streams API for edge runtimesrenderToString- Legacy synchronous rendering- Server Components - Build with async server components
- Server Rendering Overview - Compare all approaches