The one idea
SSR renders the React tree to HTML on the server for instant first paint +
SEO. STREAMING flushes that HTML as it's ready — so a slow
<Suspense> boundary stops blocking the rest of the page. The user sees the fast
shell now; the slow chunk streams in later in the same response.
Non-streaming waits for all data, then sends one blob:
first paint == full load. Streaming sends the ready shell first and keeps the connection open,
appending chunks as each Suspense boundary resolves. Hydration (hydrateRoot) then
attaches interactivity to whatever HTML has arrived.
The visualizer — fast shell + slow Suspense chunk
A page has a fast part (header + nav, ready at 100 ms) and a slow part (a Suspense boundary waiting on data that takes delay ms). Toggle streaming, drag the delay, and watch first paint vs full load diverge or merge.
1. rendering mode
Page preview (simulated)
Metrics for current mode
Non-streaming vs streaming — the timelines
Both timelines recompute from the same delay. The one matching your toggle is lit; the other is dimmed for contrast. ▲ first paint is when bytes first hit the browser; ● fully loaded is when the last chunk lands.
Render → flush → hydrate (how Start orchestrates it)
Start wires this through Nitro/Vite. The server entry picks a handler: defaultRenderHandler /
renderRouterToString (non-streaming) or defaultStreamHandler /
renderRouterToStream (streaming). Loader/server-function data is auto-dehydrated into the
HTML and rehydrated on the client.
// React tree -> HTML import { renderRouterToStream } from '@tanstack/react-router/ssr/server' // under the hood: React's // renderToPipeableStream(<App/>)
// onShellReady: send the fast shell // -> first paint (early!) // stream stays OPEN; each resolved // <Suspense> appends a chunk
import { hydrateRoot } from 'react-dom/client' hydrateRoot(document, <RouterClient router={router} />) // attaches events; selective // hydration per chunk
Comparison — mode × behavior
renderRouterToStream
+ Suspense flushes the shell early; hydrateRoot finishes the job on the client.
Cross-refs:
🔗 server_functions (where server data comes from under SSR) ·
🔗 astro_rendering_modes (the rendering-mode choice) ·
🔗 spa_vs_mpa (SPA vs SSR/MPA — TanStack spans both).