Performance Analysis of React CSR, SSR, and React 18 Streaming SSR
This article examines how different React rendering strategies—client‑side rendering, server‑side rendering, and the new React 18 Streaming SSR—affect key web performance metrics such as TTI, FCP, and first‑paint, and demonstrates substantial latency reductions achieved through streaming and selective hydration.
Author Jiang Yuheng, a front‑end engineer at Qunar, focuses on the large‑front‑end service platform and web page performance optimization.
Page performance directly influences user experience and revenue. Qunar measures web performance using Time‑to‑Interactive (TTI), First Contentful Paint (FCP) and first‑paint, with TTI being the most critical metric for user experience.
The article analyzes TTI timing under various React rendering modes and explores the performance gains brought by React 18 Streaming SSR.
Performance Metrics
FCP: the time when the browser first paints visible content (text, images, SVG, etc.).
First‑paint: the time from page navigation to the completion of the first visible rendering.
TTI: the time from page navigation to the moment the page becomes fully interactive.
Rendering Modes
1. React CSR (Client‑Side Rendering)
In CSR the server returns a static HTML containing <script> and <link> tags and an empty div . The browser downloads and executes JavaScript, React builds the component tree, and the entire page is rendered on the client.
Steps:
Client requests the page; server returns static HTML.
Browser parses HTML and downloads CSS and JS.
After CSS/JS load, the first paint (often a loading skeleton) occurs; data is fetched in componentDidMount .
When the API response arrives, the page re‑renders and becomes interactive. In CSR, first‑paint and TTI times are essentially the same.
The main drawback is a slow first‑paint because the browser must wait for JS download, execution, and data fetching.
2. React SSR (Server‑Side Rendering)
SSR generates HTML on the server, which the browser can display before JavaScript execution, reducing white‑screen time. Frameworks such as Next.js and Remix support SSR.
Process:
Server fetches data, calls renderToString to produce HTML, and sends it to the browser.
Browser parses HTML and displays content after CSS loads (FCP and first‑paint) but cannot interact yet.
When JS finishes downloading, the browser hydrates the markup, making the page interactive (TTI).
SSR improves first‑paint compared with CSR because data fetching happens on the server, but TTI still waits for JS download and hydration, yielding only a modest ~170 ms improvement.
Remaining SSR issues:
Server must wait for all backend calls before returning HTML.
Browser must wait for the complete HTML before downloading JS.
All components, even those off‑screen, must hydrate before interaction is possible.
3. React 18 Streaming SSR
React 18 introduces renderToPipeableStream , allowing the server to stream HTML fragments as they become ready. Combined with Suspense , this enables selective hydration and big‑pipe‑like behavior.
Key points in Q‑metajs (a custom React 18 framework):
Server streams non‑suspended components first; suspended components wait for their data.
Browser parses the streamed HTML, downloads CSS/JS, and displays the non‑suspended parts immediately (FCP).
When a suspended component’s data arrives, the server streams its HTML fragment and a script to mount it; the fragment appears on the page (still non‑interactive).
Selective hydrate runs for the newly streamed fragment, making it interactive (TTI).
This approach allows data fetching and asset downloading to run in parallel, dramatically reducing overall rendering time, especially when many modules depend on independent APIs.
Implementation Details
Q‑metajs provides a LazyComponent HOC that wraps a component with Suspense . On the server, the component is rendered without waiting for its data; the client receives a fallback UI. When the backend API resolves, a Promise is thrown, caught by Suspense , and the component re‑renders with the fetched data.
Key steps:
Server receives request and calls renderToPipeableStream , streaming HTML for all non‑suspended parts.
Browser parses the initial HTML, downloads CSS, shows a skeleton (FCP), and starts JS download.
After the API for a suspended component resolves, the server streams the component’s HTML fragment and the associated script.
Browser inserts the fragment, then performs selective hydration, making the component interactive.
Optimization Results
Monitoring the ticket‑change request page showed that Streaming SSR reduced the P90 TTI by 820 ms compared with traditional SSR and by 990 ms compared with CSR.
Images illustrating the timing diagrams, component flow, and performance gains are included throughout the article.
Qunar Tech Salon
Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.