What is streaming SSR and how does it work?
The problem with classic SSR
With regular (blocking) SSR, the server:
- Collects all the data (fetch / database).
- Renders the entire React component into a string (
renderToString). - Only after that sends the finished HTML to the user.
The user sees the page only after the whole render is done - even if part of the content could have been displayed earlier.
What Streaming SSR does
Streaming SSR solves this problem: instead of waiting for the whole render to finish, the server streams finished parts of the HTML as they become ready.
How it works:
- The server starts rendering the React component.
- As soon as part of the tree (for example,
<Header>or<Skeleton>) is ready - the server immediately sends it into the stream. - The browser paints these fragments instantly.
- The remaining pieces arrive gradually (for example, when the promises for data resolve).
- After the JS loads, hydration runs - the page becomes interactive.
Example in React 18
Server
javascript
import { renderToPipeableStream } from "react-dom/server";
import express from "express";
import App from "./App";
const app = express();
app.get("/", (req, res) => {
let didError = false;
const stream = renderToPipeableStream(<App />, {
onShellReady() {
res.status(didError ? 500 : 200);
res.setHeader("Content-Type", "text/html");
stream.pipe(res); // HTML starts to flow
},
onError(err) {
didError = true;
console.error(err);
},
});
});Client
javascript
import { hydrateRoot } from "react-dom/client";
hydrateRoot(document.getElementById("root"), <App />);
renderToPipeableStream()is the streaming version ofrenderToString(), which lets React send HTML in chunks as they become ready.
Advantages of Streaming SSR
| Advantage | Description |
|---|---|
| Instant First Paint | The first part of the HTML (header, skeleton) is visible right away |
| Lower TTFB / LCP | The server doesn't wait for the render to finish to start responding |
| Smooth data loading | Sections can be sent as soon as their data is ready |
| Suspense support | Components with React.Suspense are automatically streamed later |
| Edge rendering | The stream works great on a CDN / edge server |
What this looks like in the browser
javascript
[Server starts rendering App]
↓
Sends <html><head>...</head><body>
↓
Sends <Header> (ready instantly)
↓
While waiting for the API → sends <Skeleton />
↓
When data arrives → replaces <Skeleton> with <Content />
↓
Finishes the stream </body></html>The user sees:
- The interface skeleton within milliseconds,
- Content loading in smoothly,
- No "blank loading screen".
Combined with React Suspense
Streaming SSR unlocks the full potential of Suspense:
javascript
<Suspense fallback={<Loading />}>
<Comments />
</Suspense><Loading />is rendered and streamed right away,<Comments />is added later, once the data has loaded, and React correctly inserts it in place without re-rendering the whole page.
Where Streaming SSR is used
- Next.js 13+ (App Router) - used by default.
- Remix, Astro, Qwik, SvelteKit - also implement streaming SSR.
- Supported on Node.js, Deno, Edge (Cloudflare, Vercel).
Potential difficulties
| Problem | Description |
|---|---|
| Caching | Streamed responses cannot simply be cached as finished HTML |
| Errors mid-stream | It's harder to roll back HTML that has already been sent |
| Debugging complexity | It's harder to see at which stage something went wrong |
| Infrastructure | You need servers that support streaming (HTTP chunked encoding) |
Summary
Streaming SSR = "next-generation SSR":
- Delivers HTML gradually, without waiting for the render to finish;
- Lets the user see content sooner;
- Works great with React 18 + Suspense;
- But requires careful management of streams, caching, and errors.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.