Single Page Application
Concept
A way of building for the web where one document loads once and every navigation after that swaps the screen inside it.
Why it exists
On the older web, pressing a link fetched a fresh document from the server and redrew the entire screen. The address changes, whatever you were looking at gets thrown away, and a new document is drawn from scratch. For a site made of pages you read one after another, that's the natural arrangement.
The trouble showed up in tools people work inside for hours. Open a document from a list, come back to the list, and the list rebuilds from the top with the scroll position reset. You were on the twentieth item and you return to the first. Somebody who moves between a list and a document thirty times a day waits thirty times and scrolls back down thirty times.
Worse, everything living on that screen goes with it. A half-typed comment, a panel you collapsed, the filter you just chose — all of it returns to its default.
An SPA fetches the document once and changes only the part that needs changing. The waiting and the losing came from the same cause, and this removes that cause. What it doesn't remove is the cost, which moves to the first load. Every decision about this approach is a trade about where that cost lands and who ends up paying it.
How it works
The browser takes a document and a bundle of code once, and from then on it requests only the data a screen needs and edits the screen already on display.
Five places are worth separating.
- First entry. What arrives is a nearly empty document plus a JavaScript bundle. All the drawing lives inside that bundle, so a large bundle means a long wait before anything appears. This is where the entire cost of the approach collects. A bundle that grows by 200 kilobytes charges every first-time visitor, not only the screens that grew.
- Navigation. The address changes without asking the server for another document. The browser's URL gets rewritten and the code already loaded decides which screen to draw.
- Data. Each screen requests only the values it needs. Reusing values already fetched when returning to a list removes the wait completely. That same caching is what makes a second visit feel instant and a first visit feel slow.
- State. Swapping the screen leaves anything held in memory intact. Half-typed text, scroll position, and open panels survive because of that.
- Back and refresh. Work the browser used to handle now has to be handled deliberately. Pressing back and having nothing happen, or landing somewhere unrelated, is the failure that lives here. Refresh has the same shape: the address has to carry enough to rebuild the screen, or reloading drops someone back at the start.
It's easy to confuse this with the terms that divide where drawing happens. Client-side rendering (CSR), drawing screens in the browser, is not the same distinction. This one is about how navigation works; that one is about where the drawing runs.
Current practice mixes them. The server draws and sends the first screen so nothing starts blank, and navigation after that behaves like an SPA. Where server-side rendering (SSR) meets this approach, hydration becomes necessary — the few hundred milliseconds where the screen is visible but the downloaded code hasn't attached behavior to it yet, so taps do nothing.
Where it shows up
Tools where people stay on one screen and act on it constantly are the home for this. Document editors, admin consoles, dashboards where numbers get sliced repeatedly. Navigation is frequent and there's state that mustn't be lost, so what's gained outweighs the cost. For someone crossing between a list and a document thirty times a day, it's paying 1.6 seconds once to save the other twenty-nine trips.
Screens behind a login usually go this way too. Nothing there needs to appear in search results, which makes a slower first paint affordable.
Two situations argue the other way.
The first is a page people arrive at from search. This approach starts with a nearly empty document, so the content exists only after code runs. If a crawler doesn't wait that long it reads an empty page. Static site generation (SSG), building pages before deployment, or server rendering fits instead.
The second is a page someone sees once and leaves. There's no subsequent navigation to recover the cost of the bundle, so only the cost gets paid. Marketing landing pages sit squarely here, which is why they often live outside the app entirely.
Pagedeck splits these. The document editor and the admin screens behind login are built this way, while public document pages and the blog are drawn on the server and sent down.
How it sounds at work
"It reloads every time I come back to the list. Why?"
Data already fetched isn't being reused; every visit requests it again.
"First paint is 3.2 seconds and the bundle is 1.4 megabytes."
The cost this approach carries has passed what the product can absorb.
"This page has to rank in search, so it can't be built this way."
Different pages in one product get different approaches.
At Pagedeck, Theo measured first entry on the document editor at 3.2 seconds. One bundle held the code for the editor, the admin screens, and the analytics screens together.
Splitting the bundle per screen, so each arrives only when needed, brought it to 1.6 seconds. Noa settled what's visible in the meantime. If nothing is specified for the 1.6 seconds before code arrives, what ships is a blank screen. She chose a list skeleton over a spinner, because a spinner tells the reader to wait and a skeleton tells them what is coming.
What people get wrong
The most common mistake is reading this as the fast approach. Screens change without the flicker, which makes it feel fast. But what got faster is the second navigation onward, while the first paint got slower, because the bundle has to arrive before anything is drawn. Miss the distinction and a product with a three-second first paint gets reported as fast, with no way to explain why the people bought through advertising leave inside those three seconds. The honest version of the claim names both halves: slower to open, faster to use.
The second is assuming this reduces the server's work. Drawing moved to the browser, so it looks that way. But the data filling those screens still comes from the server, and requests get chopped from screen-sized into value-sized, which usually means more of them — twenty requests per screen becoming sixty smaller ones is ordinary. Pages that need to rank, and the link previews that appear when someone pastes a URL into a chat, still have to be rendered server-side. The server's work didn't shrink; it changed shape.
Easily confused with
| Term | What it decides | How it differs |
|---|---|---|
| PWA | Making a web app behave like an installed app | Concerns the shell — installation, notifications, offline behavior. An SPA decides how screens get swapped, so the two stack rather than compete |
Where to go next
- Frontend
see where rendering sits among the six branches
- Client-side rendering
the distinction about where drawing happens, side by side
Last updated 2026-09-26