"We need a Single Page Application."
A perfectly reasonable sentence, provided one does not ask the follow-up question. But let us ask it anyway: for what, precisely?
An online shop. Product catalogue, cart, checkout. The sort of thing server-side rendering handled comfortably in 1995, back when modems still made that noise and nobody thought twice about it.
But once the letters S, P, and A appear in a requirements document, something rather expensive begins to happen.
The Cascade
A Single Page Application is never just a Single Page Application. It is a commitment, one that arrives in stages, each stage presented as a natural consequence of the previous, each one adding headcount, tooling, and infrastructure cost that was not in the original estimate.
The mechanism works as follows. You choose a framework: React, Vue, or Angular. The framework requires state management, because client-side applications must keep track of what the server used to track for them. State management requires client-side routing, because the browser's native navigation no longer applies. Routing requires an API layer (REST or GraphQL) because the server now serves data rather than pages. The API requires token authentication, because sessions are suddenly someone else's problem. Authentication requires a build pipeline, because none of this ships as plain HTML. The build pipeline requires Node.js for server-side rendering, because search engines still expect actual markup. SSR requires hydration logic, because the client must silently reconstruct what the server already built. And all of the above requires DevOps. Kubernetes, probably. A platform team, definitely.
Infrastructure cost tripled, at minimum, not because users demanded faster checkouts, but because the architecture demanded babysitting.
The Scaling Paradox
Node.js cannot utilise more than one CPU core per process. This is not a controversial claim; it is a documented property of the event loop. The prescribed solution is horizontal scaling: more processes, more containers, more billable compute hours. PHP handles concurrent requests natively. Rust and Go scale vertically. Node.js scales by multiplication, and a remarkable number of people decided this was a good idea.
One might charitably call it job creation.
The Payload
The consequences arrive in the browser. According to the 2024 HTTP Archive, the median web page now ships 558 KB of JavaScript, of which 44% is unused during the initial page load. Google's own performance budget guidance recommends staying under 170 KB. The average SPA delivers triple that without breaking a sweat.
Zoom out further and the trajectory becomes rather difficult to defend.
Median JavaScript payload grew from 90 KB in 2010 to 650 KB in 2024. That is not progress. That is force-feeding marketed as growth.
The Amortisation Fallacy
The standard defence of the SPA runs approximately thus: the initial load cost is higher, certainly, but it amortises across a long session because subsequent navigations are faster. The application shell stays in memory. The investment pays off.
A compelling argument, provided one's users actually stay.
E-commerce visitors view three to five pages per session, then leave. They are gone before the app shell investment has so much as broken even. The SPA model bets on session depth that a shop simply does not produce.
A shop is not Gmail. Rather obvious, one might think. Apparently not.
The Metrics Defence
"But our metrics say otherwise!" Of course they do. When the dashboard is designed to confirm the decision that was already made, confirmation is precisely what it produces. Agile dashboards share a curious property with Soviet five-year plans: targets are always met, the methodology is never questioned, and anyone who raises the discrepancy between the numbers and observable reality is invited to recalibrate their expectations.
One does not measure the cost of the SPA by comparing SPA-to-SPA. One measures it by comparing the SPA to what the server would have delivered without one. That comparison, for some reason, never appears on the dashboard.
The Ecosystem as Business Model
npm hosts 2.1 million packages. A staggering number, until one considers how many of them solve problems that exist only within the SPA paradigm. Client-side routing libraries. State serialisation utilities. Hydration helpers. Server-component bridges. Entire categories that vanish the moment you render HTML on the server and let the browser do what browsers have done since 1993.
The complexity is not accidental. It is the business model.
Bootcamps teach React because "that is what companies want." Companies want React because "that is what bootcamps teach." The loop is self-reinforcing, economically rational for every participant, and entirely disconnected from the question of whether the user needed a Single Page Application in the first place. Splendid.
The Alternative Exists
This is not a call to return to PHP 4 and table layouts. The alternatives are modern, well-documented, and considerably lighter.
Hotwire delivers 90% server-rendered HTML with minimal JavaScript for the interactive bits. htmx weighs 14 KB and extends HTML itself rather than replacing it. Progressive enhancement, the principle that a page should work before JavaScript loads, then improve when it does, still works precisely as advertised. It simply lacks a venture-funded marketing department.
The fastest SPA is, rather often, no SPA at all.
Server-side rendering solved the online shop in 1995. Everything since has been an argument about why the solution needs to be more expensive.
The SPA tax is not a line item on an invoice. It is the invoice: the infrastructure, the headcount, the tooling, the build minutes, the platform team, the Kubernetes cluster, the on-call rota. All of it flowing from a single architectural decision that was never questioned because questioning it would have meant admitting the requirements document was wrong.
Three to five pages. Then they leave.