Nolan Lawson sparked some discussion when he described a noticeable shift away from single-page applications (SPAs):
Hip new frameworks like Astro, Qwik, and Elder.js are touting their MPA [multi-page application] with “0kB JavaScript by default.” Blog posts are making the rounds listing all the challenges with SPAs: history, focus management, scroll restoration, Cmd/Ctrl-click, memory leaks, etc. Gleeful potshots are being taken against SPAs.
I think what’s less discussed, though, is how the context has changed in recent years to give MPAs more of an upper hand against SPAs.
It seems a number of folks really clung to that first part because Nolan published a follow-up to clarify that SPAs are far from doomed:
[T]he point of my post wasn’t to bury SPAs and dance on their grave. I think SPAs are great, I’ve worked on many of them, and I think they have a bright future ahead of them. My main point was: if the only reason you’re using an SPA is because “it makes navigations faster,” then maybe it’s time to re-evaluate that.
And there’s good reason he says that. In fact, the first article specifically points to work being done on Shared Element Transitions. If they move forward, we’ll have an API for animating/transitioning/sizing/positioning elements on page entrance and exist. Jake Archibald demonstrated how it works at Google I/O 2022 and the video is a gem.
If you’re wondering how one page can transition into another, the browser takes screenshots of the outgoing page and the incoming page, then transitions between those. So, it’s not so much one page becoming another as much as it is the browser holding onto two images so it can animate one in while the other animates out. Jake says what’s happening behind the scene is a DOM structure is created out of pseudo-elements containing the page images:
<transition-container>
<image-wrapper>
<outgoing-image />
<incoming-image />
</>
</>
We can “screenshot” a specific element if we want to isolate it and apply a different animation from the rest of the page:
.site-header {
page-transition-tag: site-header;
contain: paint;
}
And we get pseudo-elements we can hook into and assign 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 />
</>
</>
Dang, that’s clever as heck!
It’s also proof in the pudding of just how much HTML, CSS, and JavaScript continue to evolve and improve. So much so that Jeremy Keith suggests it’s high time we re-evaluate our past judgment of some technologies:
If you weren’t aware of changes over the past few years, it would be easy to still think that single page apps offer some unique advantages that in fact no longer hold true. […] But developers remain suspicious, still prefering to trust third-party libraries over native browser features. They made a decision about those libraries in the past. They evaluated the state of browser support in the past. I wish they would re-evaluate those decisions.
The ingredients for SPAs specifically:
In recent years in particular it feels like the web has come on in leaps and bounds: service workers, native JavaScript APIs, and an astonishing boost in what you can do with CSS. Most important of all, the interoperability between browsers is getting better and better. Universal support for new web standards arrives at a faster rate than ever before.
HTML, CSS, and JavaScript: it’s still the best cocktail in town. Even if it takes a minute for it to catch up.