0Pricing
React Academy · Lesson

How React SSR Works Under the Hood

Trace the renderToString/renderToPipeableStream path and how the client picks up hydration.

How React SSR Works Under the Hood 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.

The Problem SSR Solves

Client-side React requires JavaScript to render. Users see a blank page until JS loads and runs. SSR pre-renders HTML on the server so users see content immediately, improving perceived performance and SEO.

renderToString

The original SSR API: renderToString(<App />) synchronously renders the component tree to an HTML string on the server.

import { renderToString } from 'react-dom/server';
import App from './App';

// In an Express handler:
app.get('*', (req, res) => {
  const html = renderToString(<App />);
  res.send(`
    <!DOCTYPE html>
    <html><body>
      <div id="root">${html}</div>
      <script src="/bundle.js"></script>
    </body></html>
  `);
});

Hydration on the Client

After the HTML arrives, the browser parses it and renders the UI. Then React's hydrateRoot() attaches event listeners and makes the static HTML interactive — this is hydration.

// client/index.tsx
import { hydrateRoot } from 'react-dom/client';
import App from './App';

hydrateRoot(document.getElementById('root'), <App />);

What Happens During Hydration

React walks the server-rendered DOM and the virtual DOM simultaneously. If they match, React attaches listeners without touching the DOM. If they differ, it logs a hydration warning and corrects the DOM.

renderToPipeableStream

React 18's streaming API sends HTML in chunks as data becomes available, allowing the browser to progressively paint content while the server is still processing.

import { renderToPipeableStream } from 'react-dom/server';

app.get('*', (req, res) => {
  const { pipe } = renderToPipeableStream(<App />, {
    bootstrapScripts: ['/bundle.js'],
    onShellReady() {
      res.setHeader('content-type', 'text/html');
      pipe(res); // starts streaming
    },
  });
});

Suspense and Streaming

With streaming SSR, Suspense boundaries stream the fallback first, then replace it with content when data resolves — without blocking the initial HTML flush.

// The shell (nav, layout) streams immediately;
// <UserProfile> suspends and streams when data resolves
function App() {
  return (
    <Layout>
      <Suspense fallback={<Skeleton />}>
        <UserProfile /> {/* streams after data loads */}
      </Suspense>
    </Layout>
  );
}

Data Serialization

Data fetched on the server must be serialized and embedded in the HTML so the client can read it during hydration without re-fetching.

// Server:
const data = await fetchData();
const html = renderToString(<App data={data} />);
const serialized = JSON.stringify(data).replace(/</g, '\\u003c');

// Inline data in HTML:
`<script>window.__INITIAL_DATA__ = ${serialized};</script>`

// Client hydration reads:
const data = window.__INITIAL_DATA__;

Environment Differences

Server code runs in Node.js, not a browser. Code referencing window, document, or localStorage crashes on the server. Guard with typeof window !== 'undefined'.

const isBrowser = typeof window !== 'undefined';

function getTheme() {
  if (!isBrowser) return 'light'; // server-safe default
  return localStorage.getItem('theme') ?? 'light';
}

React Server Components vs SSR

SSR renders components to HTML on each request. React Server Components (RSC) are different — they never ship their JS to the client and can be async, but RSC requires a bundler that supports the RSC protocol (Next.js, etc.).

Performance Benefits of Streaming

Streaming SSR improves Time To First Byte (TTFB) for the shell and enables selective hydration — the browser can hydrate interactive islands before the full page HTML has finished streaming.

Quick Check

What does React's hydrateRoot() do differently from createRoot() when the server HTML already exists?

Recap

SSR uses renderToPipeableStream to send HTML before JS loads. The client calls hydrateRoot() to attach event listeners to the server HTML. Streaming SSR with Suspense sends the shell first and fills content asynchronously. Guard browser APIs with typeof window !== 'undefined'.

Frequently asked questions

Is the “How React SSR Works Under the Hood” lesson free?

Yes — the full text of “How React SSR Works Under the Hood” 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 “How React SSR Works Under the Hood”?

Trace the renderToString/renderToPipeableStream path and how the client picks up hydration. 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 “How React SSR Works Under the Hood” 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. How React SSR Works Under the Hood
  2. Hydration Errors: Causes & Fixes
  3. Selective Hydration & Streaming HTML
  4. Islands Architecture Pattern
← Back to React Academy