The SPA Debate Needs a Refresh, Not a Eulogy

Nolan Lawson recently kicked off a lively conversation by arguing that the pendulum has swung away from single-page applications. He pointed to the rise of frameworks like Astro, Qwik, and Elder.js that advertise themselves as multi-page applications with zero kilobytes of JavaScript by default, and to the growing chorus of developers documenting the headaches of SPAs—history management, focus control, scroll restoration, modifier-click handling, and memory leaks, to name a few.

But as Lawson clarified in a follow-up post, the intent was never to declare SPAs dead. He’s built plenty of them and still believes in their future. His core point was narrower: if you’re choosing an SPA solely because it makes navigation feel faster, it may be time to reconsider that assumption.

Why the Ground Has Shifted

The technological landscape has changed considerably in recent years, and that’s exactly why the old SPA-versus-MPA calculus no longer holds. One of the most promising developments Lawson highlights is the work on the Shared Element Transitions API. If this proposal moves forward, it would give developers a standard way to animate, transition, size, and position elements when a page enters or exits—something that previously required elaborate JavaScript workarounds or was simply not feasible.

Jake Archibald demonstrated how this works at Google I/O 2022, and the mechanics are elegant. When navigating from one page to another, the browser doesn’t actually transform one DOM into the next. Instead, it captures screenshots of the outgoing and incoming pages, then animates between them. The browser creates a temporary DOM structure of pseudo-elements containing these page images to drive the transition:

<transition-container>
  <image-wrapper>
    <outgoing-image />
    <incoming-image />
  </>
</>

This approach also allows you to isolate a particular element by taking a “screenshot” of it, so you can apply a different animation to that element while the rest of the page behaves differently:

.site-header {
  page-transition-tag: site-header;
  contain: paint;
}

The API exposes pseudo-elements that you can target with custom @keyframe animations:

<!-- ::page-transition=container(root)  -->
<transition-container>
  <!-- ::page-transition-image-wrapper(root)  -->
  <image-wrapper>
    <!-- ::page-transition-outgoing-image(root) -->
    <outgoing-image />
    <!-- ::page-transition-incoming-image(root) -->
    <incoming-image />
  </>
</>

It’s an impressive piece of engineering—and a clear sign of how much native web capabilities have advanced.

Re-Evaluating Old Assumptions

That’s precisely the point Jeremy Keith makes: it may be time to revisit decisions made years ago about which technologies to trust. Many developers still reach for third-party libraries out of habit, even when native browser features now offer equivalent or better functionality. They locked in their choices based on browser support or performance characteristics that may no longer reflect reality.

For SPAs specifically, the context has shifted dramatically. Service workers are now well-established, native JavaScript APIs cover more ground than ever, CSS capabilities have grown astonishingly, and—most importantly—cross-browser interoperability has improved to the point where new web standards achieve universal support much faster than in the past.

Despite all the debate, the underlying trio of HTML, CSS, and JavaScript remains a remarkably powerful combination. The web platform may take time to catch up to what developers want from it, but when it does, it’s often worth revisiting an old decision with fresh eyes.