Skip to content
yutils

How SSR, SSG, SPA, and RSC Actually Differ

Where the HTML is built (server / build / client), TTFB vs LCP vs interactivity trade-offs, the bundle size each model ships, when to pick what, and why 'Next.js App Router' is hybrid not pure RSC.

~10 min read

"SPA / SSR / SSG / RSC / ISR / PPR" — Next.js docs are an acronym storm. They're all different combinations of "where does the page get built" + "how much JS goes to the client". This guide covers each model's precise difference, the TTFB / LCP / interactivity trade-offs, and the truth that Next.js App Router isn't pure RSC.

Four (+ Two) Models by Criterion

Criterion 1 — where the HTML is built:
  - Build time → SSG
  - Server (per request) → SSR
  - Client (browser) → SPA
  - Server, per-component → RSC

Criterion 2 — what's in the JS bundle:
  - All component code → SPA, SSR
  - Client-only components → RSC

Criterion 3 — when the user can interact:
  - After download + hydration → SSR, SSG
  - After download → SPA
  - Hydration ~0 → Qwik, Astro islands

Criterion 4 — where data is fetched:
  - On the client (useEffect / TanStack Query) → SPA
  - On the server (before page) → SSR, RSC
  - At build → SSG

SPA (Single Page Application)

Flow:
  1. server → index.html (nearly empty) + bundle.js
  2. browser → downloads bundle.js (~MB)
  3. JS executes → React/Vue/Svelte builds DOM
  4. useEffect → API fetch
  5. data arrives → render
  6. user interactive

TTFB: fast (small HTML)
LCP: slow (must wait for JS + fetch)
Interactive: slow
SEO: poor (crawlers without JS see empty HTML)

Examples: CRA, default Vite, React Router

Good for: internal dashboards, authenticated apps (SEO irrelevant)
Bad for: marketing sites, blogs, e-commerce

SSR (Server-Side Rendering)

Flow:
  1. user request → server renders React → HTML (incl. DB fetch)
  2. browser → paints HTML instantly
  3. JS bundle downloads
  4. hydration → interactive

TTFB: medium (includes server render time)
LCP: fast (HTML paints instantly)
Interactive: medium (hydration cost)
SEO: good (HTML is complete)

Examples: Next.js Pages Router, Remix, Nuxt 3

Good for: fresh data per request (dashboards, feeds)
Bad for: global sites where TTFB matters (no CDN edge = server every time)

SSG (Static Site Generation)

Flow:
  Build time: render all pages → HTML files (deploy to CDN)
  Runtime: CDN returns static HTML instantly

TTFB: very fast (CDN edge)
LCP: very fast
Interactive: medium (hydration)
SEO: excellent
Cost: nearly free (no server at idle)

Examples: Next.js generateStaticParams, Astro, Hugo, Gatsby

Good for: blogs, docs, marketing sites (low change frequency)
Bad for: frequently changing content (rebuild needed)
        personalized content (can't have per-user HTML)

ISR (Incremental Static Regeneration, Next.js)

Solves SSG's limits (rebuild time + change frequency):
  - First: SSG (static HTML)
  - After some time (revalidate=60), marked stale
  - Next request → return stale immediately + regenerate in background
  - Subsequent request → new HTML

→ "Up to 60s stale is OK" trade for SSG speed + SSR freshness.
→ Best fit for Wikipedia / news sites.

RSC (React Server Components)

Flow:
  1. user request → server builds React tree → RSC payload (text format)
  2. server starts streaming response (HTML alongside)
  3. browser paints as HTML arrives (fast TTFB)
  4. JS bundle download — no server-component code (smaller)
  5. hydration (client components only)

TTFB: fast (streaming)
LCP: fast
Interactive: fast (small JS)
SEO: excellent (HTML complete)
Bundle: small (excludes server-only code)

Examples: Next.js App Router, Remix v2+

Key novelty: smaller bundle + server logic directly in components

PPR (Partial Prerendering, Next.js 14+)

Mix part-static, part-dynamic on the same page:

  page:
    <Header />            ← static (build time)
    <UserGreeting />      ← dynamic (per request, per user)
    <Footer />            ← static

  Build: static parts pre-rendered → cached at CDN
  Runtime: on request, stream static HTML immediately + server-render dynamic parts → stream

→ Near-zero TTFB (static starts immediately) + dynamic parts streamed.
→ Combines SSR personalization with SSG speed.

Status: as of 2026-05, Next.js experimental → moving toward stable.

Trade-off Table

ModelTTFBLCPTTIBundleSEOCost
SPAFastSlowSlowLargePoorCheap (CDN only)
SSRMediumFastMediumLargeGoodExpensive (server every time)
SSGVery fastVery fastMediumLargeExcellentCheap
ISRVery fastVery fastMediumLargeExcellentCheap + regenerate cost
RSCFastFastFastSmall ✓ExcellentMedium (server logic)
PPRVery fastVery fastFastSmallExcellentMedium

Next.js App Router Isn't Pure RSC

The Next.js App Router defaults to RSC — but it's actually:
  - server components (RSC) — code doesn't reach client
  - client components ("use client") — SPA-like hydration
  - SSR — client components' first render also on the server
  - SSG — paths decided as static are built at build time
  - ISR — pages with revalidate
  - PPR — partial prerendering (experimental)

→ Next.js App Router = a hybrid of RSC + SSR + SSG + ISR + PPR.
   Mode is set differently per page / component.

This hybrid is the modern answer:
  - Server component by default (smaller bundle)
  - Client only for dynamic parts
  - SSG for infrequent updates, ISR for medium, SSR for per-request
  - PPR mixes static + dynamic streaming

Common Pitfalls

  • "We're SSR" oversimplification — reality is mixed. State each route / component's mode.
  • SSG everything — heavy rebuild burden for frequently-changing data. ISR or SSR.
  • SPA with SEO ignored — marketing / blog as SPA = hard to surface in search. SSR or SSG.
  • SSR everything — server cost ↑ + can't leverage edge cache. Consider SSG / ISR.
  • RSC "use client" too high — at page top, RSC benefit = 0. Push toward leaves.

Wrap-up

SPA / SSR / SSG / RSC differ on "where HTML" + "how much JS to the client". Modern Next.js App Router is a hybrid of 6 modes — chosen per page / component.

Practical picks: - Blogs / docs / marketing = SSG (or ISR) - Frequently-changing dashboards = SSR (or RSC) - Global, personalized = PPR + edge - Authenticated internal apps (no SEO) = SPA

Back to guides