0Pricing
React Academy · Lesson

React 18 Streaming: How renderToPipeableStream Works

Understand how renderToPipeableStream progressively flushes HTML chunks before all async data resolves.

React 18 Streaming: How renderToPipeableStream Works is a free React Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Before React 18: The Synchronous Bottleneck

Before React 18, server-side rendering used renderToString, which is synchronous: React renders the entire component tree to a string before sending any HTML to the browser. If any component loads data, the server must wait for all data before the first byte reaches the user.

React 18: renderToPipeableStream

React 18 introduced renderToPipeableStream, which renders the component tree as a stream of HTML chunks. Instead of waiting for everything to be ready, React sends HTML as it completes each part of the tree, allowing the browser to start rendering visible content immediately.

The Anatomy: pipe and abort

renderToPipeableStream returns an object with two methods: pipe(writable) and abort(). pipe accepts a Node.js Writable stream (typically the HTTP response) and starts streaming HTML to it. abort() cancels the stream, used when the request is cancelled or a timeout is exceeded.

What Streams First: The Shell

The stream begins with the HTML shell — all the content that is not blocked by a pending Suspense boundary. This includes the document head, navigation, footer, layout chrome, and any synchronously-available content. The shell streams immediately, before any data-dependent components resolve.

Suspense Fallbacks in the Stream

Components inside Suspense boundaries that are waiting for data initially stream as their fallback UI (e.g., a loading spinner). React sends a script tag later that replaces the fallback with the resolved content — all without a client-side re-render of the shell.

bootstrapScripts for Client Hydration

Pass bootstrapScripts: ['/static/js/client.js'] to renderToPipeableStream to tell React to inject a script tag at the end of the stream. The browser loads this bundle and calls hydrateRoot, attaching React's event handlers to the server-rendered DOM.

onShellReady: The Right Moment to Pipe

onShellReady fires when the HTML shell (everything outside pending Suspense boundaries) is ready to send. This is the correct moment to call pipe(res) — but importantly, set the HTTP status code and response headers before calling pipe, since streaming locks in the HTTP status.

Setting Headers Before Streaming

Because streaming begins immediately when you call pipe(), any res.setHeader or res.statusCode assignments must happen before the pipe call. A common pattern is to set status 200 and Content-Type: text/html in the onShellReady callback, then call pipe(res) immediately after.

onShellError: Fatal Errors Before the Shell

onShellError fires when an unrecoverable error occurs before the shell finishes rendering — for example, a top-level component throws outside of any Suspense boundary. In this case, you cannot stream a partial response; instead, redirect to an error page or send a static error HTML document.

onAllReady: For Bots and Crawlers

onAllReady fires when the entire React tree has resolved — including all Suspense boundaries. For search engine crawlers and social media link previewers, you want to send the complete HTML without streaming. Check if the request is from a bot and call pipe(res) in onAllReady instead of onShellReady.

Error Recovery During Streaming

onError fires for errors in Suspense boundaries — React can still stream the rest of the page while that boundary falls back to its error UI. Use onError to log the error to your observability pipeline without aborting the stream for the user.

onAllReady vs onShellReady

When should you use onAllReady instead of onShellReady to start piping?

Lesson Recap

renderToPipeableStream is React 18's streaming SSR primitive for Node.js servers. It streams the HTML shell immediately via onShellReady, fills in Suspense boundaries as data resolves, and hydrates the client via bootstrapScripts. Use onAllReady for bots and crawlers, onShellError for fatal failures, and always set HTTP headers before calling pipe().

Frequently asked questions

Is the “React 18 Streaming: How renderToPipeableStream Works” lesson free?

Yes — the full text of “React 18 Streaming: How renderToPipeableStream Works” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.

What will I learn in “React 18 Streaming: How renderToPipeableStream Works”?

Understand how renderToPipeableStream progressively flushes HTML chunks before all async data resolves. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start React Academy?

No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “React 18 Streaming: How renderToPipeableStream Works” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this React Academy lesson?

Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. React 18 Streaming: How renderToPipeableStream Works
  2. Suspense Boundaries and Streaming Priority
  3. Progressive Hydration with Selective Hydration
  4. Measuring Streaming Impact: FCP, TTI, and LCP
← Back to React Academy