Skip to content
➜cat blog/ssr-vs-ssg-when-each-wins.md

Server-Side Rendering vs Static Generation: When Each Wins

A decision framework for choosing between SSR, SSG, and ISR based on real project constraints, not marketing pages.

8 min

Every meta-framework offers SSR, SSG, and some form of incremental generation. The documentation explains how each works. What it rarely explains is when to choose each one for a real project with real constraints. After shipping projects with all three approaches, here's the decision framework I use.

The Decision Isn't Global

The first mistake is choosing a rendering strategy for the entire application. Modern meta-frameworks (Nuxt 3, Next.js 14+) let you choose per route. A marketing homepage can be static while the dashboard is server-rendered. This isn't a new feature, but teams still default to one strategy everywhere because it's simpler to reason about.

Start by listing your routes and asking one question about each: does this page's content change based on the request?

  • If no → static generation candidate
  • If yes → server rendering or client-side fetching

When Static Generation Wins

Static generation pre-renders pages at build time. The output is plain HTML files served from a CDN. No server, no cold starts, no database queries at request time.

Use it for:

  • Marketing pages, landing pages, documentation. Content changes infrequently. Rebuilds take seconds.
  • Blog posts and articles. This blog is statically generated. Each markdown file becomes an HTML page at build time.
  • Portfolio pages. Project listings, case studies, about pages. All content I control and update deliberately.

The constraint: every content change requires a rebuild and redeploy. For a personal site, that's fine since I'm already committing to git. For a CMS-driven marketing site with non-technical editors, it requires a webhook trigger from the CMS to the build pipeline.

The real advantage of SSG isn't speed (though it's fast). It's operational simplicity. No server to monitor, no scaling decisions, no cold start latency. The CDN handles everything.

When SSR Wins

Server-side rendering generates the page on every request. The server fetches data, renders the HTML, and sends it to the client.

Use it for:

  • Personalized pages. Dashboards, account settings, any page where the content depends on who's requesting it.
  • Search results and filtered listings. When the URL contains query parameters that determine the content.
  • Real-time data. Stock prices, live scores, inventory counts. Anything stale within seconds.
  • SEO-critical pages with dynamic content. Product pages on an e-commerce site where the catalog changes frequently.

The constraint: every request hits your server. You need infrastructure, monitoring, and a plan for traffic spikes.

The real advantage of SSR isn't dynamic content (you can fetch client-side for that). It's the combination of dynamic content and SEO. Search engines get the full HTML. Users get a fast first paint. The server does the work once, and the client doesn't need to show a loading spinner.

When ISR Fills the Gap

Incremental Static Regeneration (ISR), or Nuxt's equivalent with routeRules, pre-renders pages like SSG but revalidates them on a schedule.

// nuxt.config.ts
routeRules: {
  '/blog/**': { isr: 3600 },       // revalidate every hour
  '/projects/**': { prerender: true }, // fully static
  '/dashboard/**': { ssr: true },      // server-rendered
}

Use it when:

  • Content changes regularly but not in real-time
  • You want CDN performance but can't rebuild on every change
  • The page is public (not personalized) but the data updates periodically

ISR is the compromise strategy. It's not as fast as pure SSG (the first request after revalidation hits the server) and not as fresh as SSR (content can be stale up to the revalidation window). But for most content-driven pages, it's the right tradeoff.

The Framework I Use

For every new route, I ask these questions in order:

  1. Is the content the same for every visitor? → SSG
  2. Does it change, but not in real-time? → ISR with an appropriate revalidation window
  3. Does it depend on the request (user, query, cookies)? → SSR
  4. Is it only needed after the page loads (non-SEO-critical)? → Client-side fetch

Most applications end up with a mix. This portfolio uses:

  • /** → prerender: true (all pages are static)
  • server/api/** → ssr: true (API routes are server-rendered by definition)

If I added a dashboard or user-specific features, I'd add SSR rules for those routes without changing anything else.

The Mistake I See Most Often

Teams pick SSR for everything because "we might need dynamic content later." This adds server infrastructure complexity from day one for a hypothetical future requirement. Start static. Add SSR to specific routes when you actually need it. The migration path in both Nuxt and Next is trivial. Change a config line, not the page component.

The other common mistake: using client-side fetching for SEO-critical content. If a search engine needs to see it, render it on the server. Client-side fetched content may or may not be indexed depending on the crawler's JavaScript rendering budget.

What I'd Tell a Junior Developer

Don't optimize for the rendering strategy. Optimize for the user experience. Users don't care whether a page was static or server-rendered. They care that it loads fast and shows the right content. Pick the simplest approach that meets those two requirements, and move on to the problems that actually need your attention.