Rendering strategies: CSR, SSR, SSG, ISR
In this series (12 parts)
- What frontend system design covers
- Rendering strategies: CSR, SSR, SSG, ISR
- Performance fundamentals: Core Web Vitals
- Loading performance and resource optimization
- State management at scale
- Component architecture and design systems
- Client-side caching and offline support
- Real-time on the frontend
- Frontend security
- Scalability for frontend systems
- Accessibility as a system design concern
- Monitoring and observability for frontends
Every web page reaches the user through a rendering strategy. The strategy determines where HTML is generated, when it is generated, and how much work the browser must do before the user sees content. Pick wrong and you get slow loads, poor SEO, or infrastructure costs that scale linearly with traffic.
This article covers four strategies: client-side rendering, server-side rendering, static site generation, and incremental static regeneration. Each solves a real problem and creates new ones. As discussed in frontend system design fundamentals, trade-off awareness is what separates good architecture from default choices.
Client-side rendering (CSR)
CSR ships an empty HTML shell and a JavaScript bundle. The browser downloads the JS, executes it, fetches data from APIs, then renders the page. Everything happens on the client.
sequenceDiagram participant Browser participant CDN participant API Browser->>CDN: GET /page CDN->>Browser: HTML shell + JS bundle Browser->>Browser: Parse and execute JS Browser->>API: Fetch data API-->>Browser: JSON response Browser->>Browser: Render DOM with data Note over Browser: Page is now interactive
Client-side rendering sequence. The browser receives a near-empty HTML document and builds the entire page with JavaScript.
When CSR works well
CSR shines for applications behind authentication where SEO does not matter. Dashboards, admin panels, internal tools; these benefit from CSR because:
- Simple infrastructure. You serve static files from a CDN. No server-side computation per request.
- Rich interactions. Single-page application routing feels instant after the initial load.
- Easy deployment. Build once, deploy to any static host.
When CSR breaks down
CSR has a fundamental problem: the user stares at a blank page while JavaScript downloads and executes. On a fast connection with a modern device, this takes 1 to 2 seconds. On a slow connection with a budget phone, it can take 8 to 12 seconds.
Search engines have improved at indexing JavaScript-rendered content, but it remains unreliable. Critical landing pages, product pages, and blog posts should not depend on CSR alone.
The JavaScript bundle is also a scaling problem. As your application grows, the bundle grows. Without aggressive code splitting, users download megabytes of code they may never execute.
Server-side rendering (SSR)
SSR generates the full HTML on the server for each request. The browser receives a complete page that it can display immediately. JavaScript then “hydrates” the page, attaching event listeners and making it interactive.
sequenceDiagram participant Browser participant Server participant Database Browser->>Server: GET /page Server->>Database: Query data Database-->>Server: Data response Server->>Server: Render HTML with data Server->>Browser: Complete HTML + JS bundle Browser->>Browser: Display HTML immediately Browser->>Browser: Hydrate (attach event listeners) Note over Browser: Page is now interactive
Server-side rendering sequence. The server does the heavy lifting, sending a fully rendered page to the browser.
The hydration problem
SSR creates a paradox. The user sees content quickly (good TTFB, good LCP), but the page is not interactive until hydration completes. This gap, called the “uncanny valley,” means users see buttons they cannot click and forms they cannot fill.
Hydration is expensive. The browser must:
- Download the JavaScript bundle.
- Parse and execute it.
- Reconcile the server-rendered DOM with what React (or your framework) expects.
- Attach all event listeners.
For large pages, hydration can take several seconds. The user sees content but cannot interact with it. This is often worse than a loading spinner because it violates expectations.
Modern frameworks address this with selective hydration, streaming SSR, and React Server Components. These techniques allow the server to send HTML in chunks and the browser to hydrate components progressively rather than all at once.
SSR trade-offs
The server computes HTML for every request. This means:
- Higher server costs. Each page view triggers server-side rendering. Traffic spikes require scaling compute, not just bandwidth.
- Slower TTFB under load. If the server is busy, response times increase for everyone.
- Complexity. Your code must run in both Node.js and the browser. Environment-specific bugs are common.
Static site generation (SSG)
SSG generates all pages at build time. The output is plain HTML files that can be served from a CDN with zero server-side computation.
sequenceDiagram participant Build as Build Process participant CMS participant CDN participant Browser Build->>CMS: Fetch all content CMS-->>Build: Content data Build->>Build: Generate HTML for every page Build->>CDN: Deploy static HTML files Note over CDN: Files cached at edge Browser->>CDN: GET /page CDN->>Browser: Pre-built HTML Browser->>Browser: Display immediately Browser->>Browser: Hydrate if needed
Static site generation sequence. Pages are built once and served as static files from a CDN.
SSG advantages
SSG produces the fastest possible page loads. The HTML is pre-built and cached at CDN edge nodes worldwide. TTFB is typically under 50ms regardless of where the user is located.
Security is also simpler. There is no server to hack, no database to inject, no runtime to exploit. The attack surface shrinks to the CDN and any client-side JavaScript.
SSG limitations
The critical limitation is build time. If your site has 100,000 pages and each takes 200ms to build, a full rebuild takes over 5 hours. Content changes are not visible until the next build completes.
SSG is ideal for content that changes infrequently: documentation, blogs, marketing sites. It breaks down for pages that need real-time data or user-specific content.
Incremental static regeneration (ISR)
ISR combines the performance of SSG with the freshness of SSR. Pages are statically generated but can be revalidated in the background after a configurable time interval.
sequenceDiagram participant Browser participant CDN participant Server participant Database Browser->>CDN: GET /page CDN->>Browser: Cached HTML (stale) Note over CDN: Revalidation triggered in background CDN->>Server: Rebuild this page Server->>Database: Fetch fresh data Database-->>Server: Updated data Server->>Server: Generate new HTML Server->>CDN: Updated HTML Note over CDN: Next request gets fresh page
ISR sequence. The user gets the cached version immediately while a fresh version is generated in the background.
ISR uses a stale-while-revalidate pattern. The first user after the revalidation window gets the stale page (still fast) and triggers a background rebuild. The next user gets the fresh page.
This works well for product pages, news articles, and any content that changes periodically but does not need to be real-time. You get CDN-level performance with data that is at most N seconds old, where N is your revalidation interval.
ISR trade-offs
ISR requires a serverless or server environment to handle background regeneration. It is not purely static hosting. The revalidation logic adds complexity, and debugging stale content issues requires understanding the caching layer.
Comparing the strategies
The numbers above are representative for a typical content page on a 4G connection. Actual values vary based on page complexity, server location, and device capability.
| Strategy | Best for | SEO | TTFB | Dynamic data | Infrastructure |
|---|---|---|---|---|---|
| CSR | Apps behind auth | Poor | Slow | Yes | Static CDN |
| SSR | Personalized pages | Good | Medium | Yes | Server required |
| SSG | Content sites | Excellent | Fast | No | Static CDN |
| ISR | Periodically updated content | Excellent | Fast | Partial | Serverless + CDN |
Hybrid approaches
Real applications rarely use a single strategy. A typical e-commerce site might use:
- SSG for the homepage and category pages (high traffic, infrequent changes).
- ISR for product pages (frequent inventory updates, still cacheable).
- SSR for the search results page (user-specific, cannot be pre-built).
- CSR for the shopping cart and checkout (behind auth, highly interactive).
Modern frameworks like Next.js, Nuxt, and Astro support per-route rendering strategies. This is a powerful capability. The key is matching each route to the strategy that fits its constraints rather than applying one strategy globally.
graph TD
subgraph E-commerce Site
HP[Homepage - SSG] --> CAT[Category Pages - SSG]
CAT --> PDP[Product Pages - ISR]
PDP --> SEARCH[Search Results - SSR]
PDP --> CART[Cart - CSR]
CART --> CHECKOUT[Checkout - CSR]
end
style HP fill:#d4edda
style CAT fill:#d4edda
style PDP fill:#fff3cd
style SEARCH fill:#cce5ff
style CART fill:#f8d7da
style CHECKOUT fill:#f8d7da
Hybrid rendering in an e-commerce application. Each route uses the strategy that matches its requirements.
Streaming and partial hydration
The next evolution in rendering is not a new strategy but a refinement of SSR. Streaming SSR sends HTML to the browser in chunks as each component finishes rendering on the server. The browser starts painting immediately instead of waiting for the entire page.
Partial hydration takes this further by only hydrating the interactive parts of the page. A static heading does not need event listeners. A navigation menu does. By hydrating selectively, you reduce the JavaScript the browser must process.
React Server Components represent this direction. Server components run only on the server, send their output as serialized data, and never ship their code to the client. Client components handle interactivity. The boundary between them is explicit in the code.
What comes next
Choosing a rendering strategy determines when users see content. But how fast is fast enough? Core Web Vitals defines the metrics that quantify frontend performance, how to measure them accurately, and what targets to aim for.