beta

SSR vs SSG vs Prerendering: When it makes sense

22 days ago

When a React, Angular or Vue app starts having SEO problems, someone inevitably suggests "just use SSR." Sometimes that's the right call. Sometimes it's months of refactoring for a problem you could solve in a day. Each strategy makes a different trade-off between build complexity, runtime cost, and content freshness.

CSR — Client-Side Rendering

In a standard React, Angular or Vue app, rendering happens entirely in the browser:

  1. Server returns a minimal HTML shell.
  2. Browser downloads the JS bundle.
  3. Framework executes, fetches data, paints the UI.

Good at: developer simplicity, rich interactivity, no server infrastructure beyond a static file host.

Breaks down when: crawlers and bots that skip JavaScript receive the empty shell. This affects search indexing, OG previews, and any tool that parses your HTML. For exactly what crawlers see, read How a Search Crawler Actually Sees Your React App.

SSR — Server-Side Rendering

Every HTTP request triggers a server-side render: the server runs your framework code, fetches data, produces a complete HTML document, and sends it to the client. React then hydrates the page — attaches event listeners to the already-visible DOM.

Good at:

  • Crawlers, bots, and users all get the same fully rendered HTML.
  • Content is always fresh — no build step needed to reflect data changes.
  • Faster perceived FCP on content-heavy pages since HTML is ready before the JS bundle executes.

Gets complicated because:

  • You need a persistent Node.js (or equivalent) server. Deployment is no longer "upload a folder to S3."
  • Server costs scale with traffic — every request is CPU work.
  • Your code must be isomorphic. Libraries that use window, document, or browser APIs need guards; third-party components may not support SSR at all.
  • CDN caching is possible but cache invalidation becomes your problem.

Fits when: content changes per-request and per-user — authenticated dashboards, personalized feeds, real-time data.

SSG — Static Site Generation

HTML is generated once at build time. The framework fetches all data, outputs a directory of .html files, and you deploy those to a CDN.

Good at:

  • Fastest possible response time — serving a pre-built file from a CDN edge is as fast as HTTP gets.
  • Zero per-request compute cost.
  • Simple, resilient infrastructure.

Gets complicated because:

  • Build times grow with content volume — thousands of pages can mean minutes per deploy.
  • Content is stale until the next rebuild; a CMS update doesn't appear until you trigger and ship a new build.
  • Highly dynamic content requires client-side fetching on top of the static shell, reintroducing the CSR problem.

Fits when: content is mostly static, updates on a predictable schedule, and no per-user server logic is needed.

Prerendering

Prerendering works differently from SSR and SSG. It doesn't change what your app serves to real users — they still get the SPA. It intercepts bot requests and serves a pre-generated HTML snapshot instead.

The snapshot is generated by running your page in a headless Chromium instance, so it reflects the fully-executed JavaScript output.

Good at:

  • Fixes crawler and bot visibility without touching your app architecture.
  • No isomorphic code requirement.
  • Works across React, Vue, Angular, and non-framework JS setups.
  • Meta tag overrides (title, description, keywords) per URL or via regex — no code deploy.
  • Cache warming via sitemap eliminates the cold-start on first request.

Limits:

  • Doesn't improve runtime performance for real users — they still load and execute the JS bundle.
  • Snapshot freshness depends on cache TTL; very frequently updated pages need correct invalidation.

Fits when: you have an existing SPA with a crawlability or social preview problem and rewriting to SSR or SSG is a disproportionate investment.

Side-by-side

 CSRSSRSSGPrerendering
Crawler-readable HTML✓ (bots only)
Always fresh content✗ (rebuild needed)✓ (within TTL)
Server runtime requiredexternal service
App code changes needed✓ (isomorphic)✓ (build integration)
Scales to large content setsslow builds at scale
User-personalized responses

How to pick

Match the strategy to your constraint:

  • Crawlers don't see my content → Prerendering, if you already have a working SPA.
  • Building a new app, SEO from day one → SSR or SSG depending on content dynamism.
  • Content rarely changes, same for all users → SSG.
  • Content is personalized or real-time → SSR.
  • Change meta tags on hundreds of pages without a deploy → Prerendering + meta overrider.

These strategies can coexist. A Next.js app can still use prerendering for URL patterns where the server render is too slow or where you need bulk meta overrides.