Capítulo 106 de 116

Chapter 106: renderToPipeableStream

Core Idea

renderToPipeableStream(reactNode, options) renders a React tree to a Node.js stream, sending HTML to the client incrementally as pieces of the tree resolve — the Node-specific server-rendering entry point that pairs with Suspense for streaming SSR.

Key Concepts

  • Node.js streams API: returns an object with a .pipe(res) method built for Node's HTTP response stream model — this API is specifically for Node server environments, distinct from renderToReadableStream (Ch 107), which targets the Web Streams API used by edge/non-Node runtimes.
  • Streaming with Suspense: content outside any Suspense boundary streams to the client first (the shell); content inside a Suspense boundary streams in later, in place, once its data resolves — the client sees the page shell immediately rather than waiting on the slowest data to render anything.
  • Key callbacks in options: onShellReady (fires once the initial synchronous shell is ready to stream — the point at which it's typically safe to start piping to the response), onAllReady (fires once everything, including all Suspense-boundary content, has resolved — useful for non-streaming consumers like crawlers/static generation that need the complete HTML in one shot), onError (custom error logging for rendering failures).
  • Choosing onShellReady vs. onAllReady is a real trade-off: streaming from onShellReady gives the fastest Time to First Byte but requires the client to handle progressively-arriving content; waiting for onAllReady (common for bots/crawlers that can't process streaming HTML) trades speed for a single complete response.
  • Aborting: the returned object supports aborting the stream (e.g. on a timeout), letting still-pending Suspense boundaries fall back to client-side rendering instead of hanging the response indefinitely.

Code Examples

const { pipe } = renderToPipeableStream(<App />, {
  onShellReady() {
    response.setHeader('Content-Type', 'text/html');
    pipe(response);
  },
});
  • What it demonstrates: starting the HTTP response stream as soon as the initial shell is ready, rather than waiting for every Suspense boundary in the page to resolve.

Key Takeaways

  1. Use renderToPipeableStream in Node.js server environments; use renderToReadableStream (Ch 107) for Web Streams-based runtimes (edge functions, Deno, etc.).
  2. onShellReady is the streaming-optimized choice for browsers; onAllReady is the safer choice for crawlers/bots that need complete HTML.
  3. Suspense boundaries are what actually determine streaming granularity — the same page-authoring technique from Ch 70 directly controls server-streaming behavior here.

Connects To

  • Ch 70 (Suspense): the boundaries that determine what streams first vs. later.
  • Ch 107 (renderToReadableStream): the Web Streams equivalent for non-Node runtimes.
  • Ch 95 (hydrateRoot): the client-side counterpart that takes over this server-streamed HTML.