Web Development
SSR and SSG: Next.js, Nuxt
SPA: the user sees a white screen for 2-3 seconds while JS loads, then another 1-2 seconds for data. Search engines see an empty page. Next.js with RSC and Streaming SSR: HTML arrives in 50ms, data streams as it becomes ready, JS bundle is minimal. This is not an optimization - it is a different model.
- **Shopify** migrated to RSC for storefronts - 35% JS bundle reduction, LCP improved by 40%
- **Vercel** - the company behind Next.js - builds its entire dashboard on RSC + Streaming SSR
- **GitHub** uses Turbo Frames (a similar pattern) for partial page loading without full reload
React Server Components
Before React Server Components (RSC), all component code was sent to the browser - including libraries only needed for rendering. **React Server Components** execute only on the server: their code never reaches the client's JS bundle. The result: a smaller bundle, faster First Load JS, and direct database access without an API round-trip.
In Next.js 13+ App Router, all components are Server Components by default. A client component is marked with the `'use client'` directive. An RSC can render Client Components, but not the reverse - RSC cannot be imported from Client Components.
**RSC vs SSR:** SSR executes components on the server but still sends their JS to the browser for hydration. RSC sends no JS at all - only a serialized render result (RSC payload).
A React Server Component makes a direct query to PostgreSQL. What is sent to the browser?
Hydration and Island Architecture
SSR returns HTML - the page is visible immediately. But HTML is static: buttons do not click, forms do not submit. **Hydration** is the process of bringing the page to life: the browser loads JS, React attaches to the existing DOM, and event handlers are added. Until hydration completes, the page looks ready but is not interactive.
**Island Architecture** solves the problem of hydrating an entire page: instead of one large JS bundle for the whole page, interactivity is added only to specific "islands" - components that actually need JS. Static content remains plain HTML. Astro implements this pattern natively.
**Partial hydration - the future:** Progressive Enhancement + Island Architecture allows shipping minimal JS. An e-commerce site on Astro - 90% static HTML, 10% interactive islands. Result: TTI 2-3x better than an SPA.
After SSR the page is displayed but buttons do not respond to clicks. The reason:
ISR: Incremental Static Regeneration
SSG generates HTML at build time - maximum speed, but data goes stale. SSR generates on every request - always fresh data, but server load. **ISR (Incremental Static Regeneration)** is the compromise: pages are static but automatically regenerated in the background on a schedule or on demand.
ISR works on a **stale-while-revalidate** model: the first request after TTL expiry gets the stale page (fast!) while background regeneration starts. The next request gets the fresh page. No user ever waits for regeneration.
**ISR vs Cache-Control:** ISR is a Next.js-specific abstraction over CDN caching. Under the hood it uses `s-maxage` + `stale-while-revalidate` headers. Vercel, Netlify, and Cloudflare Pages natively support this semantics.
An ISR page with revalidate=60. Page was generated at 10:00:00. A request arrives at 10:00:30, another at 10:01:30. What does the second user receive?
Streaming SSR and Suspense
Classic SSR waits for all data to load, then sends the full HTML. If one request is slow, the entire page is delayed. **Streaming SSR** sends HTML in chunks: fast content arrives immediately, slow parts follow when ready.
React Suspense is the mechanism to declare "this part is waiting for data". With Streaming SSR, React first sends HTML with a fallback (skeleton/spinner), then when data is ready, streams an HTML patch that replaces the fallback with real content. The browser starts rendering immediately.
**Streaming + TTFB:** Time to First Byte with Streaming SSR is milliseconds (the page shell is sent immediately). Without streaming, TTFB equals the slowest query time. Google factors TTFB into Core Web Vitals.
SSR is always slower than CSR (client-side rendering) due to server load.
SSR with Streaming and RSC delivers better TTFB and FCP than CSR. The server is closer to data; there are no browser-side request waterfalls.
CSR: browser loads JS (200-500ms), then makes API requests (100-300ms), then renders. SSR: server renders on the first request with data already nearby. With proper caching the gap is enormous.
A page has two Suspense boundaries: fast (50ms) and slow (2s). When does the user see the fast content?
SSR and SSG: Next.js, Nuxt
- RSC executes only on the server: direct DB access, code never enters the client JS bundle
- Hydration brings SSR HTML to life via JS; Island Architecture adds JS only to interactive components
- ISR: static pages + background regeneration on TTL or on-demand webhook; stale-while-revalidate
- Streaming SSR + Suspense: HTML streams in chunks, fast content visible without waiting for slow parts
Related Topics
SSR and SSG are part of the broader frontend performance ecosystem:
- Frontend Performance — Core Web Vitals measure the effect of SSR and Streaming
- HTTP and CDN — ISR is implemented via CDN cache-control headers
- State Management — RSC changes architecture: server state vs client state
Вопросы для размышления
- How to determine which component should be a Server Component and which a Client Component?
- In what situations is ISR preferable to SSR with CDN-level caching?
- How does Streaming SSR change component design - what must be considered when splitting into Suspense boundaries?