Skip to main content
The journal
StrategyAugust 202613 min

The routing library, the transition framework, and the JavaScript positioning library were responses to gaps in a browser platform that has since closed most of them.

Single-page architecture assembled client-side answers to specific browser gaps: the History API was too thin for routing, full-page navigation produced visual jarring, and floating elements required a JavaScript positioning library. The browser has since closed most of those gaps, with the Navigation API reaching cross-browser interoperability in January 2026 and same-document view transitions in October 2025. Teams starting projects in the second half of 2026 are working with a platform that is substantially different from the one those defaults were written for.

By
Julian R. Mountford
Founder & Chairman
The routing library, the transition framework, and the JavaScript positioning library were responses to gaps in a browser platform that has since closed most of them.

The term 'single-page application' has always been doing more work than it appears to. As a label, it names a rendering decision (the server delivers a shell, the client renders into it), a routing model (URL updates do not trigger full document navigations), a transition architecture (moving between views without page reloads), and an implicit theory of the browser: that the browser's native navigation behaviour is too coarse for the kind of experience a competitive digital product requires. All four of these components arrived together not because they are logically inseparable but because the browser gaps that required them appeared simultaneously, and the solutions came bundled. Teams that adopted SPA architecture in the years around 2014 to 2016 adopted all four components as a package. Whether they needed all four, or only some of them, is a question the original decision's momentum tends not to leave room for.

The argument in this piece is not that SPA architecture is wrong, or that teams should migrate away from the frameworks they have built on, or that the libraries the ecosystem assembled over the past decade were misconceived. The argument is narrower: three of the four components that justified the SPA's founding choices have now reached cross-browser interoperability at the platform level, and the fourth is approaching it. Teams that chose this architecture in 2016 because the browser could not provide these capabilities are now running something built around constraints that no longer fully hold, and the costs of that architecture remain even as its original rationale has narrowed. The distinction matters most for teams beginning new projects, where the choice is genuinely open.

#02The sequence the browser gave, and when

The History API's pushState method arrived as part of the HTML5 specification and was implemented across the major browsers in the years around 2011. Before it, no mechanism existed for a web page to modify the browser's displayed URL without loading a new document. After it, JavaScript could call history.pushState, the address bar would update, and no navigation would occur. This was the technical seed from which SPA routing grew, and the browser gave it while withholding most of what would have made it sufficient. The History API provided access to the history stack but no principled model of what the entries in that stack represented. It offered no native mechanism for intercepting user-initiated navigation before it committed. It conflated the browser's concept of document location with the application's concept of view state in ways that produced well-documented inconsistencies at back-button interactions and scroll restoration. The community built routing libraries around it anyway, because full-page navigation produced a visible reload on every view change that the SPA pattern existed specifically to avoid.

The routing libraries of the following decade, across every major framework, were implementations of the same workaround. React Router, Vue Router, Angular's router, Ember's router: each intercepted link clicks, prevented the browser's default navigation event, called pushState with the new path, managed component rendering and clean-up, and tried to reproduce the accessibility behaviours, focus management, and scroll restoration that document navigation handles automatically. This is not a criticism of any of them. The browser offered a thin primitive and these libraries completed it. They were built for the browser that existed, and they were built well. The depth of consensus across competing ecosystems is evidence of how genuine the underlying gap was.

The SPA rationale extended beyond routing. Smooth transitions between views were possible in SPA architectures because the JavaScript layer had continuous control over the DOM and could animate between states without interruption. Server-rendered multi-page architectures produced a perceptible reload on every navigation, and avoiding it was a central engineering rationale for client-side rendering. JavaScript positioning libraries appeared because the browser had no native mechanism for placing a floating element relative to a named anchor while detecting viewport overflow and adjusting direction: a capability essential for tooltips, dropdowns, and context menus, and one that required a runtime calculation in JavaScript after the browser had already completed layout. The Popover API did not exist. CSS anchor positioning did not exist. Same-document view transitions did not exist. SPA architecture was, to a substantial degree, the community's collected response to a browser that had not yet become what it has since become.

#03What the platform supplied between 2024 and 2026

The Popover API achieved cross-browser Baseline status in 2024. It provides native top-layer placement for interactive overlay elements, handling toggle behaviour, Escape key dismissal, light-dismiss on outside clicks, and accessible attribution without any JavaScript positioning code. Before it, any tooltip or dropdown that needed to appear above other content required either a portal pattern that hoisted the element outside the component tree, or a JavaScript positioning library calculating coordinates after paint. The visual consequence of calculating position after paint is the brief reposition effect that users notice without necessarily being able to name: the element appears, the browser recalculates, and it jumps to where it was supposed to be. The browser's top-layer mechanism removes this class of problem by placing the element before paint.

CSS anchor positioning reached Baseline newly available status in 2026, with Chrome, Firefox, and Safari all shipping support. The anchor-name and position-anchor properties allow a positioned element to be placed relative to a named element in the document, and the @position-try rule handles overflow detection and directional flipping. The browser's layout engine performs these calculations as part of the normal layout pass. For several years the same calculations had been performed at runtime in JavaScript, by libraries whose existence was predicated on the absence of anchor positioning from the CSS specification. Those libraries are not obsolete: they address positioning within broader component systems whose scope extends well beyond the geometry calculation. But the geometry calculation itself, which was the original justification for their existence, is now in the platform.

Same-document view transitions reached cross-browser Baseline newly available status in October 2025. The View Transition API allows developers to capture the current visual state of a document, render the next state, and animate the difference, with the view-transition-name property identifying elements that should morph rather than simply cross-fade. SPA architectures had provided this capability through framework animation systems and third-party transition libraries, and its availability was one of the specific reasons cited for preferring client-side rendering over server-rendered page navigations. With same-document transitions at Baseline, an application that updates its view through a server request and a partial DOM update has access to the same visual continuity quality without any client-side rendering layer.

The Navigation API arrived last, reaching Baseline newly available status in January 2026 when Firefox completed its implementation. The API's opening documentation describes it as specifically aimed at the needs of single-page applications, which is a precise framing: it acknowledges that SPAs had needs the platform could not serve, and the API was built to serve them. It provides navigate events that fire before any navigation commits, allowing JavaScript to intercept, redirect, or abort. It gives the application a principled history entries list that represents genuine navigation history rather than an application's manipulation of the history stack. It is, in effect, the platform implementation of what every major SPA routing library was individually providing on top of the History API for the previous decade and a half.

Platform news of this kind arrives as individual items in individual release notes, across months and years, and rarely presents itself as a cumulative change to the architectural calculation. Browser release notes announce features; they do not announce that a category of library dependency has been substantially replaced. Whether four individual arrivals together constitute something worth reconsidering is something that tends not to surface in the architecture review of a team building confidently against a settled stack.

#04The gap that remains

Cross-document view transitions are not yet cross-browser. The @view-transition CSS at-rule with navigation: auto, which enables automatic animated transitions between separately served pages without any JavaScript, is available in Chrome and Safari but has not yet reached full interoperability in Firefox, and is therefore not at Baseline status as of mid-2026. The consequence is that an application relying on cross-document view transitions will have smooth animated transitions in two major browser engines and standard full-page navigation in Firefox. For most applications, that graceful degradation is acceptable: the transition is an enhancement to the experience, not a dependency of the content. But for teams considering a fully server-rendered multi-page architecture specifically because they want the animated navigation experience that previously required a client-side rendering layer, the platform's story is not yet complete.

This is worth stating plainly rather than smoothing over. The case for multi-page architecture as a full visual substitute for SPA architecture on transition-heavy interfaces depends on cross-document view transitions achieving full cross-browser support, and that has not happened. What has happened is that same-document transitions are cross-browser, the Navigation API is cross-browser, anchor positioning is cross-browser, and the Popover API is cross-browser. The progress is real and substantial. The remaining gap is specific and finite. Both of those things are true simultaneously.

#05What the frameworks still provide

There is a version of this argument that concludes more cleanly: the platform has provided everything the SPA era was built to supply, JavaScript frameworks for the web are a previous generation's solution, and teams should migrate to server rendering and native capabilities. That version is tempting to construct because it has a satisfying shape, and it is largely wrong. The degree to which it is wrong is the most important part of the picture.

The component model is the SPA era's durable contribution. React, Vue, Svelte, and their contemporaries gave the trade a principled way to reason about interfaces as composed, independently testable units, with explicit boundaries, collocated state, and predictable rendering cycles. A complex form with interdependent validation across many fields benefits from the component model's discipline in ways that are independent of whether the browser can now handle navigation natively. The component framework addresses how to organise and manage complex interactive state within a view; the Navigation API addresses how to move between views. These are different problems, and the Navigation API's arrival does not answer the component problem.

Fine-grained reactivity, in the form provided by Vue's composition API or Svelte's reactive declarations, addresses a class of interface complexity that the DOM's native event model handles less efficiently. For interfaces where many elements must respond to changes in shared state without re-rendering the whole document, the reactivity system is a genuine capability that does not have a direct platform equivalent. The platform is better at managing this in 2026 than it was in 2016, but it has not fully resolved the problem the reactivity systems were built for.

The developer tooling that accumulated around SPA frameworks over the decade is also a real consideration. Hot module replacement during development, component-level unit testing with stable rendering interfaces, typed prop systems, and integration with design tooling: these matured inside the SPA ecosystem and are not yet fully matched by what is available for server-rendered alternatives. The gap is narrower in 2026 than it was in 2020, but it remains a genuine factor for teams making architectural choices based on the working environment they can offer their engineers.

The honest account of where the platform stands is something like this: it has provided native equivalents for the routing, transition, and floating-element positioning problems that originally made client-side rendering the default. It has not provided a native equivalent for the component model, the reactivity system, or the developer tooling. Teams building complex, highly interactive applications have platform-independent reasons for choosing a component framework. Teams building content-heavy, form-based, or documentation-oriented applications whose interactive complexity is modest are more likely, in choosing a framework in 2026, to be repeating a decision that was correct for a previous generation of browser rather than evaluating the decision against the current one.

#06The cost the architecture carries forward

SPA architecture carries costs that were accepted as justified in proportion to the browser gaps the architecture was filling. The question of whether those costs remain proportionate as the gaps close is worth examining without the assumption that the original trade-off still holds.

The most visible is the initial JavaScript bundle. A server-rendered page can be read before a single line of JavaScript executes; a client-rendered SPA cannot be used until the framework runtime, the routing library, the component tree, and the code-split modules for the initial view have all been parsed, compiled, and executed. On a fast device on a fast connection, this is typically imperceptible. On a mid-range device on a variable mobile network, which represents the plurality of web traffic in most markets, it is the source of the first-contentful-paint delay that performance audits have documented since these architectures became dominant. That cost was proportionate when the alternative was a page architecture incapable of smooth view transitions. With same-document transitions and the Navigation API now in the platform, the comparison is not the comparison it was.

There is also a category of accessibility costs that client-side routing has carried since its introduction. When a client-side navigation occurs, the browser does not automatically announce the new page to screen reader users, does not move focus to the incoming content region, and does not restore scroll to the correct position. These behaviours are automatic on document navigation and require explicit implementation in client-side routing. Every major routing library addresses this; not all do so correctly in all configurations; all require the developer to understand what the browser would have done natively and to replicate it deliberately. The Navigation API does not remove this responsibility entirely, but it provides the interception events and transition hooks that make a correct implementation substantially easier to reason about than the History API ever allowed.

Teams that chose this architecture because the browser could not provide these capabilities are now running something built around constraints that no longer fully hold, and the costs remain even as the original rationale has narrowed.

Teams that chose this architecture because the browser could not provide these capabilities are now running something built around constraints that no longer fully hold, and the costs remain even as the original rationale has narrowed.

The history of web architecture, reviewed with some distance, is a history of the platform catching up to the community's workarounds. CSS flexbox arrived after a decade of float-based layouts that approximated what flexbox would eventually provide. CSS custom properties arrived after preprocessors had normalised the concept of variables in stylesheets. The Fetch API standardised what XMLHttpRequest had made possible in a less consistent form. In each case, the platform captured the community's consensus answer to a genuine problem and made it available without the dependency; in each case, the prior solutions did not immediately disappear, because real systems were running on them and migration is not free. The pattern does not mean those prior solutions were mistakes. It means they were responses to a platform at a particular moment, and the platform kept moving.

The current generation of that pattern is playing out across routing and transitions. The community built routing libraries because the browser lacked a principled navigation model. The browser now has one: the Navigation API, at Baseline since January 2026. The community built transition systems because full-page navigation produced visual jarring that client-side rendering removed. Same-document view transitions, at Baseline since October 2025, provide that animation capability without requiring client-side rendering. The community built JavaScript positioning libraries because the browser could not place a floating element relative to an anchor and detect overflow. CSS anchor positioning, at Baseline in 2026, performs that in the stylesheet. None of these arrivals cancel what was built before them. They change what new decisions are being made against.

For some applications, the full framework stack remains the right choice in 2026, because the component model and reactivity system address real complexity the platform has not yet replicated. For others, the platform has closed enough of the original gap that a server-rendered approach with progressive enhancement is the more proportionate starting point. The distinction is worth drawing carefully, because it is the distinction between choosing a framework because it provides something the platform genuinely does not, and choosing it because that is what was chosen the last time the question was open.

The platform took the better part of a decade and a half, from the History API's arrival through to the Navigation API's Baseline interoperability in January 2026, to close most of what the SPA routing library was built to provide. In those years, the community built excellent libraries that served the gap well, and the ecosystem that accumulated around those libraries extended well beyond the original gap into component models, state management, and developer tooling that carry their own enduring value. Whether any given team is maintaining its current architecture because it needs what the framework provides, or because changing has a cost and nobody has recently examined what the original decision was responding to: only that team knows. The platform record now provides enough to make the question answerable. Whether it gets asked is a separate matter.

About the author
Julian R. Mountford
Founder & Chairman

Every piece in the Journal is written personally by a senior practitioner, drawing on the engagement that motivated it. No ghostwriters, no content team, no models. If a paragraph here resonates with a problem you are looking at, the author is the person to reply to — direct lines beat anonymous inboxes.

Get in touch with the practice