Understanding React 18 Streaming SSR and Selective Hydration

React 18 introduces Streaming SSR, allowing the server to send HTML in chunks and perform selective hydration, which improves performance by rendering ready sections early and handling asynchronous components via Suspense, with detailed examples of code implementation, error handling, and JS/CSS integration.

ByteDance Web Infra
ByteDance Web Infra
ByteDance Web Infra
Understanding React 18 Streaming SSR and Selective Hydration

Feature Overview

React 18 adds a new server‑side rendering mode called Streaming SSR, which streams HTML to the browser in chunks and enables selective hydration of already rendered parts.

Basic Principles

Using renderToPipeableStream on a Node.js API, the server can send the initial HTML shell, then later stream the content of async components once their data or code is ready.

let didError = false;
const stream = renderToPipeableStream(
  <App />, 
  {
    bootstrapScripts: ["main.js"],
    onShellReady() {
      res.statusCode = didError ? 500 : 200;
      res.setHeader('Content-Type', 'text/html');
      stream.pipe(res);
    },
    onError(err) {
      didError = true;
      console.error(err);
    },
  }
);

Streaming HTML Example

By setting the HTTP response header Transfer‑Encoding: chunked, the server can write the first segment, pause, then write the second segment, demonstrating progressive rendering.

const http = require("http");
const server = http.createServer(async (req, res) => {
  if (req.url === "/") {
    res.write("<div>First segment</div>");
    await sleep(2000);
    res.write("<div>Second segment</div>");
    res.end();
  }
});
server.listen(8080);

Selective Hydration

Before React 18, SSR could not split code; developers either omitted SSR for lazy components or waited for all bundles before hydration. With selective hydration, only the parts that are ready are hydrated, while Suspense boundaries pause until their async children resolve.

import { lazy, Suspense } from "react";

const Content = lazy(() => import("./Content"));

export default function App() {
  return (
    <html>
      <body>
        <div>App shell</div>
        <Suspense fallback={<Spinner />}>
          <Content />
        </Suspense>
      </body>
    </html>
  );
}

Fallback and Error Handling

If a Suspense child throws during SSR, the server replaces the placeholder comment <!--$?--> with <!--$!--> and sends a script that calls the component’s _reactRetry function on the client, allowing a graceful fallback to client‑side rendering.

Data Structures

The implementation relies on four core structures: Segment (a piece of streamed HTML), SuspenseBoundary (a boundary that tracks pending tasks), Task (a unit of rendering work), and Request (the top‑level object that coordinates the whole process).

Main Flow

1. createRequest builds the initial request, root segment, and root task. 2. startWork begins rendering, creating additional tasks for async Suspense children. 3. performWork processes the task queue, handling promises by re‑queueing tasks. 4. flushCompletedQueues writes completed segments to the response, respecting the order of boundaries. 5. When all tasks are finished, the stream is closed.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ReactServer-side RenderingCode SplittingSuspenseSelective Hydrationstreaming SSR
ByteDance Web Infra
Written by

ByteDance Web Infra

ByteDance Web Infra team, focused on delivering excellent technical solutions, building an open tech ecosystem, and advancing front-end technology within the company and the industry | The best way to predict the future is to create it

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.