Skip to content

SPA vs MPA: The Fundamental Trade-Offs

Decision criteria for single-page vs multi-page architectures, navigation, data, SEO, and team shape, without turning the choice into a framework war.

5 min read
SPA
MPA
Architecture
Frontend
Rendering
Navigation

TypeScript tooling is settled enough to stop being the weekly argument. The next question I actually hear in kickoffs is quieter and more expensive: is this a single-page app, a multi-page app, or a hybrid that pretends to be neither?

SPA vs MPA is not "React vs PHP." It is a bet about where navigation and HTML come from, who owns the first paint, and how much client state you are willing to babysit. Framework marketing collapses those into tribe membership. I want decision criteria.

What I mean by the words

MPA (multi-page): each meaningful URL is a document request. The server (or static host) returns HTML for that route. Navigation is a full document load unless you sprinkle progressive enhancement. Classic Rails/Laravel, many content sites, a lot of "boring" admin tools.

SPA (single-page): the shell loads once; route changes are client-side. Data arrives as JSON (or similar). The browser owns the transition. Create React App era apps, many dashboards, "app-like" products.

Hybrids (MPA with islands, SPA with SSR, frameworks that do both) exist on purpose. They are not a dodge, they are an admission that pure SPA and pure MPA each fail specific jobs. I still start from the pure poles so the hybrid has a reason.

The axes that matter

I score a product on five axes before picking a default:

AxisSPA leansMPA leans
Interaction densityHigh, filters, canvases, live panelsLow, read, submit, leave
Auth + session modelLong-lived client session, optimistic UIRequest/response, cookie forms fine
SEO / shareable HTMLNeeds SSR or careful pre-renderNatural document HTML
Team skill centerStrong JS/TS frontend ownershipStrong server templates + light JS
Deploy surfaceStatic/CDN + API, or BFFServer that can render HTML per request

If four of five point the same way, stop debating frameworks and pick the architecture. Framework choice is downstream.

SPAs win when in-app motion is the product: switching tabs without losing local UI state, keyboard-heavy workflows, multi-panel tools. The cost is the empty shell problem, first load pays for the runtime before content, unless you add SSR/streaming later.

MPAs win when each URL is a finished thought: article, settings form, receipt. The browser's native navigation, back-forward cache, and "view source shows the thing" debugging are underrated. The cost is re-fetching chrome and re-running boot scripts unless you invest in fragment strategies or islands.

A failure mode I see: teams pick SPA because "it feels modern," then spend a quarter recreating document semantics (titles, meta, focus management, scroll restoration) the browser already offered.

Data loading: who is the source of truth?

In an MPA, the server render is often the first data fetch. Forms POST; the next HTML reflects success or errors. Caching lives closer to HTTP.

In an SPA, you invent a data layer: fetch on mount, global caches, invalidation, loading spinners, error boundaries. Tools like TanStack Query exist because that layer is real work, not because JSON is fashionable.

I ask: how many independent data dependencies does a typical screen have? One coherent document → MPA or SSR document model. A dashboard with five endpoints and partial refresh → SPA (or a hybrid with a serious server component story later).

SEO and HTML that other machines can read

If marketing pages, docs, or public profiles need reliable HTML without executing your bundle, MPA or SSR-backed routes pay off immediately. Client-only SPA for public content is a tax you will pay in pre-render config, bot drama, or both.

Authenticated app behind a login wall? SEO is mostly irrelevant. Do not let a blog checklist force SSR on an internal tool.

Team shape beats preference

Architecture that the team cannot operate is wrong regardless of purity:

  • A backend-heavy team with thin frontend capacity will ship faster with MPA templates plus small JS enhancements.
  • A frontend-leaning product team owning interaction design will waste cycles fighting template languages if every pixel change needs a server deploy ritual.

I have watched both mismatches produce the same symptom: a "temporary" second stack that becomes permanent.

A concrete decision sketch

text
Is the primary job "document + form" with rare rich widgets? → MPA (or MPA + islands for the widgets) Is the primary job "application shell" with continuous client state? → SPA (Vite/React baseline is fine; add SSR when HTML/SEO/first paint demand it) Do public routes and app routes coexist? → Split by route: documents for public, SPA (or SSR app) for the product Prefer clear boundaries over one mega-framework config nobody owns

The split is allowed. Forced unity is how you get a blog rendered like a dashboard and a dashboard shipped like a brochure.

Failure modes

SPA as default for content sites. You will rebuild MPA features poorly.

MPA for highly interactive tools. You will smuggle a SPA in via jQuery soup or a mounting React root on every page without routing discipline.

"We'll add SSR later" with no owner. Later never comes; Core Web Vitals become a quarterly surprise.

Framework wars as proxy. Next vs Remix vs "vanilla Vite" arguments that never name navigation, data, or SEO are cosplay.

Takeaway

Pick SPA or MPA from interaction density, HTML requirements, and team shape - then pick a framework. Reversing that order is how you inherit complexity you cannot explain in a design review.

Next: the simplest honest SPA baseline I still reach for: Vite without a meta-framework, and when that simplicity stops being free.