The question behind every strategy
A browser cannot show a page until it has HTML to render. The rendering strategies differ in who builds that HTML and when: a server at request time, a build process ahead of time, or the browser itself after downloading JavaScript. A second question follows: once the HTML is on screen, how much JavaScript must run before the page responds to input?
Google's guide to rendering on the web, first published in 2019 and last updated on January 5, 2026, remains the clearest neutral reference on these trade-offs (source 1). Its central advice has not changed: measure first, prefer server rendering or static rendering over full rehydration where you can, and mix strategies per page rather than choosing one for the whole app.
Client-side rendering
With client-side rendering (CSR), the server returns a small HTML shell and one or more script bundles. The browser downloads and runs the scripts, which fetch data, build the page and handle routing.
Strengths. Servers stay simple and cheap, often a static file host and an API. After the first load, navigations can feel instant because the app already has its code and only fetches data. Caching the app shell, for example with a service worker, speeds up repeat visits (source 1).
Costs. Nothing useful appears until the JavaScript has downloaded and run and the data has arrived. The amount of JavaScript tends to grow with every feature, and on slower phones parsing and running it takes a long time. The rendering guide notes that this makes good Interaction to Next Paint harder to achieve and harder to keep, especially on mobile (source 1). Search engines can render JavaScript, but client-rendered pages need more testing to be sure crawlers see what users see.
Good fit. Apps behind a login with long sessions, such as editors, dashboards and internal tools, where first-load speed matters less than responsiveness during use, and where pages do not need to be indexed.
Server-side rendering
With server-side rendering (SSR), the server builds the full HTML for each request. The browser can paint content as soon as the HTML arrives. If the page is interactive, the framework then loads JavaScript and hydrates the markup, attaching event handlers and state.
Strengths. Content appears quickly, and for pages that need little interactivity, less JavaScript is shipped, which helps Total Blocking Time and Interaction to Next Paint (source 1). Crawlers and link-preview bots receive complete HTML.
Costs. Building HTML per request takes server time, which delays the first byte, and dynamic pages are harder to cache. If the whole page hydrates, the browser still downloads and runs code for every component, and the page can look ready while it cannot yet respond. That gap is the subject of our hydration guide.
Good fit. Pages with personalized or fast-changing content that also need to show content quickly, such as product pages with live stock, account overviews or search results.
Static generation
Static generation (SSG, also called prerendering) builds HTML at build time, one file per URL, which a CDN can serve from close to the user.
Strengths. A fast, consistent first byte and simple, cheap hosting. If pages ship little JavaScript, blocking time stays low (source 1).
Costs. Every URL must be known and built ahead of time, which becomes slow for very large sites, and personalized content cannot be baked in. Pages still pay hydration costs if they hydrate fully.
Good fit. Documentation, marketing pages, blogs, and any route whose content changes on a schedule rather than per request.
Incremental and on-demand regeneration
Frameworks blur the line between static and server rendering by regenerating static pages after deployment. In Next.js, Incremental Static Regeneration rebuilds pages in the background; version 16.3 added a mode where a page not prerendered at build time serves an instant loading shell on its first visit and then upgrades to a fully prerendered page that later visitors receive from the cache (source 2).
Streaming server rendering
Streaming sends HTML in chunks as the server produces it, instead of waiting for the whole page (source 1). The browser can paint the first chunk, typically the layout and above-the-fold content, while slower parts are still being rendered. In React, Suspense boundaries mark where the stream can pause and later fill in content; React 19.2 reveals server-rendered Suspense boundaries in short batches and stops batching as page load approaches 2.5 seconds, so throttling does not hurt Core Web Vitals (source 3).
Streaming pairs naturally with selective hydration: each boundary can hydrate independently, so the parts a user reaches first can become interactive before the rest.
Strengths. Early paint even when some data is slow; the first byte arrives sooner than with blocking SSR.
Costs. More moving parts. Status codes and headers must be decided before the body streams, error handling inside streamed sections needs care, and some proxies or CDNs buffer responses and undo the benefit unless configured.
Partial prerendering and cached shells
A newer pattern combines static and dynamic rendering within one route. The static parts of a page are prerendered and served immediately, often from a CDN, and the dynamic parts are rendered at request time and streamed in.
React 19.2 provides the low-level APIs: prerender produces a static prelude plus a postponed state, and resume later continues rendering into a stream (source 3). Next.js builds on this with Cache Components: components marked with 'use cache' become part of a prerendered shell, while dynamic components wrapped in Suspense fill in at request time. In Next.js 16.3 this sits behind the cacheComponents flag, alongside Partial Prefetching, which lets a link prefetch as much or as little of the target page as needed (source 2). Since 16.4 the Next.js team recommends the model for every app and enables it by default in new projects, ahead of making it the default in Next.js 17 (source 5).
Good fit. Routes that are mostly shared content with a few personalized parts, such as a product page with a personalized price or a header with a signed-in user's name.
Islands
Island architecture starts from the opposite direction. The page is server-rendered or static HTML, and only specific interactive components, the islands, load JavaScript and hydrate. In Astro, a component stays as plain HTML unless given a client directive such as client:idle, which loads it when the browser is idle, or client:visible, which loads it only when it enters the viewport (source 4). Server islands, marked with server:defer, let a slow or personalized fragment render separately while the rest of the page is served and cached immediately (source 4).
Strengths. Very little JavaScript for content-heavy pages, and interactivity limited to where it is needed.
Costs. Interactive components cannot share state as easily as in a single app tree, and highly interactive apps end up with so many islands that the model offers less.
A comparison at a glance
| Strategy | First byte | First content | JavaScript shipped | Main risk |
|---|---|---|---|---|
| Client-side rendering | Fast (static shell) | Late, after scripts and data | Whole app | Slow start and growing bundles |
| Server rendering | Slower (work per request) | Early | Whole page if fully hydrated | Hydration gap, server cost |
| Static generation | Fast and steady | Early | Whole page if fully hydrated | Build size, no personalization |
| Streaming with selective hydration | Fast first chunk | Early, in stages | Less on the critical path | Complexity, buffering proxies |
| Partial prerendering | Fast (cached shell) | Early shell, dynamic parts streamed | Depends on client components | New APIs, careful cache design |
| Islands | Fast | Early | Only the islands | Shared state across islands |
How to choose, route by route
Start with an inventory of routes, then ask four questions of each.
- Does the content differ per user or per request? If not, static generation or a cached shell is the natural default.
- How often does it change? Content that changes hourly can be regenerated; content that changes per request needs server rendering or streaming for those parts.
- How interactive is it? A page with one search box should not hydrate a whole app. A complex editor may be best client-rendered after a fast server-rendered shell.
- Does it need complete HTML for crawlers and previews? Public pages generally do; signed-in tools generally do not.
Then measure. Record a trace of the route on a throttled CPU with its current strategy, change one route, and compare traces and field data. The rendering pipeline visualizer shows the general shape of each strategy, but your own trace is the only reliable guide to your own app.
Common mistakes
- One strategy for everything. Moving an entire app to server rendering because one landing page needed it, and paying hydration costs on every signed-in screen.
- Ignoring hydration. Celebrating a faster first paint after moving to SSR without checking how long the page stays unresponsive afterwards.
- Caching nothing. Rendering pages per request that could be cached for minutes, so the first byte grows under load.
- Streaming through a buffering proxy. Adopting streaming while a CDN or reverse proxy holds the response until it is complete.
Sources
- Rendering on the Web, web.dev (updated January 5, 2026) Primary source (Google)
- Next.js 16.3, Next.js blog (August 3, 2026) Primary source (Vercel)
- React 19.2, React blog (October 1, 2025) Primary source (React team)
- Islands architecture, Astro Docs Primary source (Astro)
- Next.js 16.4, Next.js blog (October 6, 2026) Primary source (Vercel)