Skip to content

SSR vs SSG vs CSR Tradeoffs

advanced14 min read

The Rendering Trilemma

Every page you ship makes a fundamental tradeoff: freshness vs speed vs interactivity. Pick wrong, and you get a fast page that shows stale data, a dynamic page that takes 3 seconds to load, or a perfectly fresh page that Google can't index.

Here's the thing most people miss: this isn't a one-time architectural decision. In a modern Next.js 15 app, you pick the rendering strategy per page or even per component. The question isn't "should my app use SSR or SSG?" It's "what does this specific page need?"

The Rendering Spectrum:

Build time ←————————————————————————→ Request time
   SSG          ISR          SSR          CSR

Fastest TTFB                        Freshest data
Best cacheability                   Most personalized
Stale until rebuild                 Slowest TTFB

The Mental Model

Mental Model

Think of rendering strategies like food preparation at a restaurant.

SSG is a buffet. All the food is prepared before anyone arrives. Service is instant, but everything was made hours ago. Great when the menu rarely changes.

SSR is made-to-order cooking. Every dish is prepared fresh when you order it. You wait a bit, but you get exactly what you asked for with the freshest ingredients.

CSR is like getting raw ingredients delivered to your table with a recipe card. Your browser (the diner) does all the cooking. The kitchen sends stuff fast, but you're staring at an empty plate until the cooking finishes.

ISR is a buffet with a chef who periodically replaces stale dishes with fresh ones. Most diners get instant buffet speed, and the food is never more than N minutes old.

SSG: Static Site Generation

The HTML is generated once at build time. Every user gets the same pre-built file, typically served from a CDN edge node.

// Next.js 15 App Router: Static by default
// This page is SSG automatically — no special config needed
export default async function BlogPost({ params }) {
  const post = await getPost(params.slug)

  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}

// Tell Next.js which pages to pre-generate
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map(post => ({ slug: post.slug }))
}

What happens at build time:

  1. Next.js calls generateStaticParams to get all slugs
  2. For each slug, it renders the page to HTML
  3. The HTML + RSC payload are saved as static files
  4. At request time, the CDN serves the pre-built file — no server execution

Performance characteristics:

  • TTFB: 10-50ms (CDN edge)
  • LCP: Excellent (HTML arrives complete)
  • Server cost: Near zero (static files)

When SSG wins:

  • Content changes infrequently (docs, blog posts, marketing pages)
  • Page content is the same for every user
  • SEO matters and you want the fastest possible TTFB
  • You have a known, finite set of pages

When SSG fails:

  • User-specific content (dashboards, profiles)
  • Real-time data (stock prices, live scores)
  • Thousands of pages that change hourly (build time becomes a bottleneck)
Quiz
A marketing team publishes 5 blog posts per week. Each post gets 100K views. Which rendering strategy minimizes server cost while keeping content fresh?

SSR: Server-Side Rendering

The HTML is generated fresh on every request. The server fetches data, renders React components, and sends complete HTML to the browser.

// Next.js 15 App Router: Force dynamic rendering
export const dynamic = 'force-dynamic'

export default async function Dashboard({ params }) {
  const user = await getCurrentUser()
  const analytics = await getAnalytics(user.id)

  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <AnalyticsChart data={analytics} />
    </div>
  )
}

In the App Router, a page becomes SSR when it uses dynamic functions like cookies(), headers(), or searchParams, or when you explicitly set dynamic = 'force-dynamic'.

What happens per request:

  1. User requests the page
  2. The server executes the component (fetches data, renders React tree)
  3. HTML is streamed to the browser (with streaming SSR)
  4. Browser shows the HTML immediately, then hydrates for interactivity

Performance characteristics:

  • TTFB: 100-500ms (depends on data fetching + server location)
  • LCP: Good (HTML arrives with content, but slower than CDN)
  • Server cost: Linear with traffic (every request = server work)

When SSR wins:

  • Personalized content (user dashboards, feeds)
  • Data that changes frequently and must be fresh per request
  • SEO-critical pages with dynamic data
  • Pages that depend on request context (cookies, auth, geolocation)

When SSR fails:

  • Content that's the same for everyone (SSG is faster and cheaper)
  • High-traffic pages where server cost scales linearly
  • Pages where 1-second-old data is acceptable (ISR is more efficient)
Quiz
Your e-commerce product page shows personalized pricing based on the user's location and membership tier. Which rendering strategy is most appropriate?

CSR: Client-Side Rendering

The server sends a minimal HTML shell with a JavaScript bundle. The browser downloads, parses, and executes JS to render the page.

'use client'

import { useState, useEffect } from 'react'

export default function AdminPanel() {
  const [data, setData] = useState(null)

  useEffect(() => {
    fetch('/api/admin/stats')
      .then(res => res.json())
      .then(setData)
  }, [])

  if (!data) return <LoadingSkeleton />

  return <StatsGrid data={data} />
}

What happens:

  1. Browser receives a near-empty HTML file
  2. Downloads the JavaScript bundle (50-500KB+)
  3. JS executes, React mounts, components render
  4. Data fetches fire, components re-render with data
  5. User finally sees content

Performance characteristics:

  • TTFB: Very fast (tiny HTML shell)
  • LCP: Poor (content only appears after JS download + execution + data fetch)
  • FCP: Poor (blank screen until JS runs)
  • SEO: Terrible (search engines see empty HTML)

When CSR is appropriate:

  • Behind-auth dashboards where SEO doesn't matter
  • Highly interactive apps (editors, IDEs, design tools)
  • Real-time collaboration features
  • When the page is essentially a desktop app in the browser

When CSR is wrong:

  • Any page that needs SEO
  • Content-heavy pages (articles, documentation, landing pages)
  • Pages where first-load performance matters
  • Mobile users on slow connections
Why pure CSR is almost always wrong in 2025

Pure CSR made sense in the early SPA era when the alternative was full-page reloads. But modern frameworks like Next.js give you SSR and SSG with the same React component model. There's no DX penalty for server rendering.

The waterfall problem kills CSR performance: HTML download → JS download → JS parse/execute → data fetch → render. That's 4 sequential network-dependent steps before the user sees content. With SSR, the server does the data fetch and render in one step, sending complete HTML in the first response.

The only legitimate reason for pure CSR today is when you genuinely cannot server-render — like a canvas-based design tool where the UI is 100% JavaScript-driven and SEO is irrelevant.

The Decision Framework

Here's the actual decision tree you should walk through for every page:

Does the content change per user?
├── Yes → Does SEO matter?
│   ├── Yes → SSR (with streaming for slow data)
│   └── No → CSR is acceptable (but SSR still preferred)
└── No → How often does the content change?
    ├── Rarely (days/weeks) → SSG
    ├── Sometimes (minutes/hours) → ISR
    └── Constantly (seconds) → SSR with short cache

The Tradeoff Matrix

Strategy | TTFB   | Freshness | SEO | Server Cost | Personalization
---------|--------|-----------|-----|-------------|----------------
SSG      | Best   | Worst     | Best| Lowest      | None
ISR      | Great  | Good      | Best| Low         | None
SSR      | Good   | Best      | Good| Highest     | Full
CSR      | Fast*  | Best      | Bad | Low         | Full

* Fast TTFB but slow LCP (content appears late)

Production Scenario: The E-Commerce Platform

A team is building an e-commerce site with these page types:

  1. Homepage — Same for everyone, updates when marketing pushes new campaigns
  2. Product listing — Filters by category, shows inventory counts
  3. Product detail — Shows reviews, pricing, availability
  4. Cart — Fully personalized, real-time inventory checks
  5. Checkout — User-specific, payment-sensitive

Here's the correct rendering strategy for each:

Homepage       → SSG + on-demand revalidation (marketing triggers rebuild)
Product listing → ISR (revalidate every 60s for inventory freshness)
Product detail  → ISR (revalidate every 60s) + client-side for real-time stock
Cart           → SSR (personalized, needs auth context)
Checkout       → SSR (security-sensitive, must be fresh, personalized)

The mistake teams make? Using SSR for everything because "we need fresh data." The homepage doesn't need SSR — it's the same for every user. Product pages don't need SSR — a 60-second-old inventory count is fine. Only the cart and checkout actually need request-time rendering.

Quiz
A news site has 10,000 articles that get updated 2-3 times per day. They get 5 million page views daily. Which strategy minimizes cost while keeping content reasonably fresh?

Common Mistakes

What developers doWhat they should do
Using SSR for every page because fresh data seems safer
SSR has linear server cost scaling. A page that's the same for all users should be served from a CDN, not re-rendered per request.
Use SSG/ISR for pages with shared content, SSR only for personalized or real-time pages
Using CSR for content-heavy pages to avoid server complexity
CSR creates a 4-step waterfall (HTML, JS, execute, fetch) before content appears. SSR sends complete HTML in step 1.
Use SSR or SSG — the DX is identical in Next.js App Router and performance is dramatically better
Choosing one rendering strategy for the entire application
Different pages have different data freshness and personalization requirements. A blog post and a user dashboard have nothing in common.
Choose per page or even per component with Next.js hybrid rendering
Assuming SSG means content is always stale
On-demand revalidation lets you trigger page regeneration from a CMS webhook — the page is stale only between the content change and the webhook firing.
Combine SSG with on-demand revalidation or ISR to get CDN speed with fresh content

Key Rules

Key Rules
  1. 1SSG generates HTML at build time. Best for content that is the same for all users and changes infrequently. Fastest TTFB, lowest server cost.
  2. 2SSR generates HTML per request. Required for personalized or request-dependent content. Higher server cost, always fresh.
  3. 3CSR renders in the browser after JavaScript downloads. Only appropriate for behind-auth interactive apps where SEO is irrelevant.
  4. 4ISR combines SSG speed with periodic freshness. Pages are served from cache and regenerated in the background after a revalidation window.
  5. 5In Next.js 15 App Router, pages are static by default. They become dynamic when you use cookies(), headers(), searchParams, or set dynamic = force-dynamic.
  6. 6Choose rendering strategy per page, not per app. Most production apps use a mix of SSG, ISR, and SSR across different routes.
1/7