https://nolanlawson.com/2022/06/27/spas-theory-versus-practice/ Read the Tea Leaves Software and other dark arts, by Nolan Lawson [Search this Blog ] Search * Home * Apps * Code * Talks * About << Style scoping versus shadow DOM: which is fastest? 27 Jun SPAs: theory versus practice Posted June 27, 2022 by Nolan Lawson in performance, Web. Tagged: spas. 5 Comments I've been thinking a lot recently about Single-Page Apps (SPAs) and Multi-Page Apps (MPAs). I've been thinking about how MPAs have improved over the years, and where SPAs still have an edge. I've been thinking about how complexity creeps into software, and why a developer may choose a more complex but powerful technology at the expense of a simpler but less capable technology. I think this core dilemma - complexity vs simplicity, capability vs maintainability - is at the heart of a lot of the debates about web app architecture. Unfortunately, these debates are so often tied up in other factors (a kind of web dev culture war, Twitter-stoked conflicts, maybe even a generational gap) that it can be hard to see clearly what the debate is even about. At the risk of grossly oversimplifying things, I propose that the core of the debate can be summed up by these truisms: 1. The best SPA is better than the best MPA. 2. The average SPA is worse than the average MPA. The first statement should be clear to most seasoned web developers. Show me an MPA, and I can show you how to make it better with JavaScript. Added too much JavaScript? I can show you some clever ways to minimize, defer, and multi-thread that JavaScript. Ran into some bugs, because now you've deviated from the browser's built-in behavior? There are always ways to fix it! You've got JavaScript. Whereas with an MPA, you are delegating some responsibility to the browser. Want to animate navigations between pages? You can't (yet). Want to avoid the flash of white? You can't, until Chrome fixes it (and it's not perfect yet). Want to avoid re-rendering the whole page, when there's only a small subset that actually needs to change? You can't; it's a "full page refresh." My second truism may be more controversial than the first. But I think time and experience have shown that, whatever the promises of SPAs, the reality has been less convincing. It's not hard to find examples of poorly-built SPAs that score badly on a variety of metrics (performance, accessibility, reliability), and which could have been built better and more cheaply as a bog-standard MPA. Example: subsequent navigations To illustrate, let's consider one of the main value propositions of an SPA: making subsequent navigations faster. Rich Harris recently offered an example of using the SvelteKit website (SPA) compared to the Astro website (MPA), showing that page navigations on the Svelte site were faster. Now, to be clear, this is a bit of an unfair comparison: the Svelte site is preloading content when you hover over links, so there's no network call by the time you click. (Nice optimization!) Whereas the Astro site is not using a Service Worker or other offlining - if you throttle to 3G, it's even slower relative to the Svelte site. But I totally believe Rich is right! Even with a Service Worker, Astro would have a hard time beating SvelteKit. The amount of DOM being updated here is small and static, and doing the minimal updates in JavaScript should be faster than asking the browser to re-render the full HTML. It's hard to beat element.innerHTML = '...'. However, in many ways this site represents the ideal conditions for an SPA navigation: it's small, it's lightweight, it's built by the kind of experts who build their own JavaScript framework, and those experts are also keen to get performance right - since this website is, in part, a showcase for the framework they're offering. What about real-world websites that aren't built by JavaScript framework authors? Anthony Ricaud recently gave a talk (in French - apologies to non-Francophones) where he analyzed the performance of real-world SPAs. In the talk, he asks: What if these sites used standard MPA navigations? To answer this, he built a proxy that strips the site of its first-party JavaScript (leaving the kinds of ads and trackers that, sadly, many teams are not allowed to forgo), as well as another version of the proxy that doesn't strip any JavaScript. Then, he scripted WebPageTest to click an internal link, measuring the load times for both versions (on throttled 4G). So which was faster? Well, out of the three sites he tested, on both mobile (Moto G4) and desktop, the MPA was either just as fast or faster, every time. In some cases, the WebPageTest filmstrips even showed that the MPA version was faster by several seconds. (Note again: these are subsequent navigations.) On top of that, the MPA sites gave immediate feedback to the user when clicking - showing a loading indicator in the browser chrome. Whereas some of the SPAs didn't even manage to show a "skeleton" screen before the MPA had already finished loading. Screenshot from conference talk showing a speaker on the left and a WebPageTest filmstrip on the right. The filmstrip compares two sites: the first takes 5.5 seconds and the second takes 2.5 seconds Screenshot from Anthony Ricaud's talk. The SPA version is on top (5.5s), and the MPA version is on bottom (2.5s). Now, I don't think this experiment is perfect. As Anthony admits, removing inline