Skip to main content

renderToReadableStream

renderToReadableStream renders a React tree to a ReadableStream of HTML using the Web Streams API. This is the recommended approach for edge runtimes, Cloudflare Workers, Deno, and other modern JavaScript environments.
This API uses Web Streams API (ReadableStream) instead of Node.js streams. For Node.js, use renderToPipeableStream instead.

Reference

renderToReadableStream(element, options?)

Renders a React element to a ReadableStream with full Suspense support and progressive streaming.

Parameters

  • element: ReactNode - The React element to render
  • options: Object (optional)
    • identifierPrefix: string - Prefix for IDs generated by useId
    • namespaceURI: string - Namespace URI for the document (e.g., SVG)
    • nonce: string | { script?: string, style?: string } - Nonce for Content Security Policy
    • bootstrapScriptContent: string - Inline script to run before other scripts
    • bootstrapScripts: Array<string | { src: string, integrity?: string, crossOrigin?: string }> - External scripts to load
    • bootstrapModules: Array<string | { src: string, integrity?: string, crossOrigin?: string }> - External modules to load
    • progressiveChunkSize: number - Size of chunks for progressive streaming
    • signal: AbortSignal - Signal to abort the render
    • onError: (error: mixed, errorInfo: ErrorInfo) => ?string - Error handler callback
    • importMap: ImportMap - Import map for module scripts
    • formState: ReactFormState | null - Form state for progressive enhancement
    • onHeaders: (headers: Headers) => void - Callback when headers are ready
    • maxHeadersLength: number - Maximum length for early hints headers

Returns

Returns a Promise that resolves to a ReactDOMServerReadableStream:
  • The Promise resolves when the shell (initial UI) is ready
  • The stream continues emitting chunks as Suspense boundaries resolve
  • stream.allReady resolves when all content, including Suspense boundaries, is complete

Caveats

  • Async API: Returns a Promise that must be awaited
  • Edge optimized: Designed for edge runtimes using Web Streams
  • No pipe method: Use the stream directly with Response or other Web APIs
  • One-time use: The stream can only be read once

Usage

Basic streaming with edge runtime

Streaming with Suspense

Error handling

Aborting renders

Use AbortSignal to cancel rendering when the client disconnects:

Waiting for all content (SEO)

Wait for allReady when serving content to search engine crawlers:

Custom headers with onHeaders

Advanced Patterns

Streaming with transformations

Streaming with compression

Streaming HTML with document wrapper

Runtime Differences

Optimized for:
  • Vercel Edge Functions
  • Cloudflare Workers
  • Deno Deploy
  • Edge runtimes without Node.js APIs

Common Issues

ReadableStream can only be read once. Don’t try to use the same stream multiple times:
Solution: Generate a new stream for each request.
When using onHeaders, ensure you’re setting headers before creating the Response:
If rendering fails before the shell is ready, the Promise rejects:
If a Suspense boundary never resolves, allReady will hang:
Solution: Add timeouts and proper error handling for async operations.

Performance Best Practices

Start streaming as soon as the shell is ready:
Control streaming granularity with progressiveChunkSize:
Smaller = more granular streaming, larger = fewer chunks
Keep Suspense boundaries small and focused:

See Also