SPA vs MPA vs SSR vs SSG (vs ISR)

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: The acronym soup of how web pages are rendered, untangled. SPA, MPA, SSR, SSG, ISR — five names for fundamentally different strategies for delivering pages to users.


In plain English

When you visit a website, your browser ends up with HTML it can render. But that HTML can be produced in very different ways: by a server right now, by a server during a build that happened yesterday, by JavaScript running in the browser, or by some combination of those.

Each strategy has trade-offs in: how fast the first page appears, how fast subsequent navigation is, how fresh the data is, how much the server has to work, how SEO-friendly the page is, and how much complexity the developer takes on.

The five terms you’ll see:

  • MPA (Multi-Page App) — traditional. Each page is its own HTML file (or generated fresh on each request). Full reload between pages.
  • SPA (Single-Page App) — JavaScript builds the page in the browser after a single initial HTML load. Subsequent navigation updates parts of the page; no full reload.
  • SSR (Server-Side Rendering) — HTML is generated on the server fresh for each request, then sent to the browser. Page is interactive after JavaScript loads (“hydration”).
  • SSG (Static Site Generation) — HTML is generated once at build time. Same HTML served to everyone until the next build.
  • ISR (Incremental Static Regeneration) — Static, but with periodic background refresh. “Built once, refreshed after N seconds in the background.”

Modern frameworks like Next.js support all of these — and you can mix them in the same app, choosing per page what fits best.


Why it matters

You won’t write a framework yourself, but you’ll see all these acronyms in docs, tutorials, and Vercel dashboards. Understanding which applies to a given page helps debug behavior, improve performance, and choose the right strategy when adding new features.

The choice has real impact:

  • SSG is fastest for users but staleness matters
  • SSR is freshest but slowest
  • SPA feels smooth in-app but is bad for SEO and first paint
  • MPA is simple and SEO-friendly but feels older
  • ISR is the modern hybrid

The five strategies, side by side

MPA — Multi-Page App (the classical web)

Each URL is a separate HTML page. The browser requests /about → gets full HTML → renders it. Clicking a link goes to /contact → full page load.

Examples: classic WordPress, Wikipedia, Hacker News, most pre-2015 websites.

User clicks /about     →  Server generates or serves HTML
                       ←  Browser renders HTML (full page paint)

User clicks /contact   →  Server generates or serves HTML
                       ←  Browser renders HTML (full page paint, again)

Pros: simple, SEO-friendly by default, each page is independent. Cons: every navigation is a full reload; perceived slower; harder to do smooth animated transitions.

SPA — Single-Page App (React without a framework)

The server sends one HTML page (mostly empty), plus a JavaScript bundle. The JavaScript runs in the browser, fetches data via APIs, and builds the page. Subsequent navigation is handled by JavaScript (router), updating part of the page without full reload.

Examples: classic Create React App, vanilla Vite + React projects, Gmail, Trello.

User visits site       →  Server sends near-empty HTML + JS bundle
                       ←  Browser loads JS, fetches data, renders page

User clicks /about     →  JS intercepts click, fetches /about data
                       ←  Browser updates page contents (no reload)

Pros: smooth in-app navigation, native-app feel. Cons: slow first paint (must load JS + fetch data before anything shows), bad for SEO without extra work, complex client state.

SSR — Server-Side Rendering

The server generates fresh HTML for each request, including all the data the page needs. Sends complete HTML to the browser. The browser displays it immediately; then JavaScript “hydrates” (attaches event handlers).

Examples: Next.js App Router by default, Remix, traditional PHP, Ruby on Rails.

User visits /post/123  →  Server runs your code, queries DB, renders HTML
                       ←  Browser displays HTML immediately
                       →  Browser downloads JS
                       ←  React hydrates — page becomes interactive

Pros: fast first paint, SEO-friendly, data is always fresh. Cons: server has to do work on every request, response time = server compute + DB time + network.

SSG — Static Site Generation

HTML is generated once at build time and saved as static files. Every visitor gets the same HTML. No server-side computation per request.

Examples: typical Astro sites, Hugo, Jekyll, Gatsby (mostly), Next.js with static export.

At build time:         →  Server generates all pages from data
                       →  Static HTML files stored on CDN

User visits /post/123  →  CDN serves static HTML (very fast)
                       ←  No server processing per request

Pros: blazingly fast, cheap to host, scalable trivially. Cons: data is as fresh as the last build (could be days old), every content change needs a rebuild.

ISR — Incremental Static Regeneration (the modern hybrid)

SSG, but pages can be regenerated in the background after a stale period. First request after the stale period: serve the cached version, kick off a regeneration. Future requests get the fresh version.

At build time:         →  Generate pages, mark "valid for 60 seconds"

User visits /post/123  →  Serve cached (fast)

[60 seconds pass]

Next visit /post/123   →  Serve stale (fast); kick off background refresh
[Background]           →  Regenerate page, update cache

Next visit /post/123   →  Serve fresh (still fast)

Pros: SSG speed + freshness updates without manual rebuild. Cons: first visitor after stale period gets old version; complexity in cache invalidation.


A practical decision tree

Which to use for what kind of page?

Page typeBest fit
Marketing pages, blog postsSSG or ISR
User dashboard, account settingsSSR
Real-time chat, live feedsSPA (with WebSocket) or SSR + client subscription
Product pages (e-commerce)ISR (rebuild on inventory change)
Search resultsSSR (different per query) or SPA (client-side search)
Admin toolsSPA is fine (SEO doesn’t matter for admin)
Stripe checkout, bankingSSR (fresh, can’t be cached)

Next.js App Router lets you choose per-page or even per-component. The same app can have static marketing pages, server-rendered dashboards, and client-rendered widgets.


In Next.js terms (App Router, 2026)

By default in the App Router, components are Server Components that render on the server. Whether the result is SSR or SSG depends on whether you cache it:

// Static (SSG-like) — cached at build, regenerated on demand
"use cache";
async function getPost(id: string) {
  return await db.query("SELECT * FROM posts WHERE id = $1", [id]);
}
// Dynamic (SSR-like) — runs on every request
async function getCurrentUser() {
  // No "use cache" — always fresh
  return await getServerSession();
}

"use cache" (Next 16’s new directive) is opt-in caching. Combined with cache lifetimes (cacheLife), you control where on the SSR/SSG/ISR spectrum each route lives.

Client Components (with "use client") push parts of the page into the browser — SPA-style islands inside otherwise server-rendered pages.


What “hydration” means

You’ll see this word a lot in SSR contexts. It’s the handoff between server-rendered HTML and client-side React:

  1. Server renders the page → HTML sent to browser
  2. Browser displays HTML (user sees content immediately)
  3. JavaScript bundle loads in the background
  4. React traverses the existing DOM and “attaches” event handlers, state, etc.
  5. Page is now interactive

Hydration mismatch errors happen when the HTML the server sent doesn’t exactly match what React thinks should be there. Usual culprits: Date.now(), Math.random(), browser-only APIs, locale-dependent formatting. See Next.js for the workarounds.


A concrete example: a marketing site + blog + dashboard

Mixed Next.js app:

PageStrategyWhy
/ (homepage)SSG with ISR (refresh every 24h)Rarely changes, must be fast for SEO
/blog/[slug]SSG with ISR (refresh on publish)Content updates occasionally
/dashboardSSRUser-specific, must be fresh
/dashboard/settingsSSR + client-rendered formSame as above
/search?q=...SSR (or client-side search)Different per query
/adminSPA-style (Client Components)SEO doesn’t matter, complex interactivity

Same Next.js project. Different strategies per page. Best fit per page.


Comparing first-paint times

Roughly (for a typical page on a fast connection):

StrategyTime to first paintTime to interactive
SSG from CDN50-200ms200-800ms
ISR (cached hit)50-200ms200-800ms
SSR200-800ms400-1500ms
MPA (server-rendered)200-800ms200-800ms
SPA100-300ms HTML, then 1-3s before content2-5s

SSG and ISR are the speed champions because the work has already happened. SSR is fast for first paint of dynamic data. SPA is slow because everything happens client-side. MPA is fast paint but no smooth in-app navigation.


Common gotchas

  • “SPA = React, MPA = anything else” is wrong. Plenty of MPAs use React (Next.js Pages Router in older mode). Plenty of SPAs don’t use React. The terms describe rendering strategy, not the library.

  • SEO with SPAs is hard. Search engines have improved at running JS, but it’s still inconsistent. SSR or SSG is much safer.

  • use client doesn’t mean “SPA.” In Next.js App Router, "use client" marks a component for client-side execution; the rest of the page is still server-rendered. The result is hybrid, not pure SPA.

  • Hydration assumes server and client output match. Any divergence breaks. Common cause: rendering timestamps that differ between server and client.

  • SSG isn’t great for personalized content. Same HTML for everyone. For per-user content, use SSR or client-side rendering.

  • ISR cache invalidation can confuse. A user updates a post but sees the old version because the cache hasn’t refreshed yet. Use revalidatePath to invalidate explicitly when data changes.

  • “Streaming SSR” complicates the mental model. Modern Next.js streams parts of the page as they’re ready. First paint can happen before the whole page is computed. Mostly a win, occasionally surprising.

  • Mixed strategies = mixed cache behavior. Knowing whether a particular page is SSG, SSR, or ISR matters when debugging “why don’t my changes show up.”

  • Vercel preview deployments may behave differently from production. Cache settings, environment variables, edge regions can all differ. Test in preview if behavior is unexpected.

  • JavaScript bundles for SPAs grow huge. A 500KB+ JS bundle dominates load time on mobile. Modern frameworks code-split automatically, but SPAs still ship more than SSR.

  • “Static” can still query a database. SSG happens at build time but can fetch from your DB then. The build is the query; the runtime is just serving HTML.

  • The right answer changes per page. Resist picking one strategy for everything. The whole point of modern frameworks is mixing.

  • Old tutorials use Pages Router terminology. getStaticProps (SSG), getServerSideProps (SSR), getStaticPaths (dynamic SSG). App Router replaces these with the use cache directive + Server Components. Don’t mix idioms.


See also

Sources