https://nolanlawson.com/2022/05/29/state-is-hard-why-spas-will-persist/ Read the Tea Leaves Software and other dark arts, by Nolan Lawson [Search this Blog ] Search * Home * Apps * Code * Talks * About << More thoughts on SPAs 29 May State is hard: why SPAs will persist Posted May 29, 2022 by Nolan Lawson in Web. Tagged: spas. Leave a Comment When I write about web development, sometimes it feels like the parable of the blind men and the elephant. I'm out here eagerly describing the trunk, someone else protests that no, it's a tail, and meanwhile the person riding on its back is wondering what all the commotion is down there. We're all building so many different types of products using web technology - e-commerce sites, productivity apps, blogs, streaming sites, video games, hybrid mobile apps, dashboards on actual spaceships - that it gets difficult to even have a shared vocabulary to describe what we're doing. And each sub-discipline of web development is so deep that it's easy to get tunnel-visioned and forget that other people are working with different tools and constraints. This is what I like about blogging, though: it can help solve the problem of "feeling out the elephant." I can offer my own perspective, even if flawed, and summon the human hive-mind to help describe the rest of the beast. My last two posts have been a somewhat clumsy fumbling toward a new definition of SPAs (Single-Page Apps) and MPAs (Multi-Page Apps), and why you'd choose one versus the other when building a website. As it turns out, there is probably enough here to fill a book, but my goal is just to bring my own point of view (and bias) to the table and let others fill in the gaps with their comments and feedback. I have a few main biases on this topic: 1. I usually prize performance over ergonomics. I'll go for the more performant solution, even if it's awkward or unintuitive. 2. I like understanding how browsers work, and relying on the "browser-y" way of doing things rather than inventing my own prosthetic solution. 3. I don't pay nearly enough attention to what's happening in "user land" - I like to stay "close to the metal" and see the world from the browser's perspective. Show me your compiled code, not your source code! In thinking about this topic and reading what others have written on it, one thing that struck me is that a big attraction for SPAs is the same thing that can cause so many problems: state. People who like SPAs often celebrate the fact than an SPA maintains state between navigations. For instance: 1. You have a search input. You type into it, click somewhere else to navigate, and the next page still has the text in the input. 2. You have a scrollable sidebar. You scroll halfway down, click on something, and the next page still has the sidebar at the last scroll position. 3. You have a list of expandable cards. You expand one of them, click somewhere else, and the next page still has the one card expanded. Note that these kinds of examples are particularly important for so-called "nested routes", especially in complex desktop UIs. Think of sidebars, headers, and footers that maintain their state while the rest of the UI changes. I find it interesting that this is much less of an issue in mobile UIs, where it's more common to change (nearly) the whole viewport on navigation. Managing state is one of the hardest things about writing software. And in many ways, this aspect of state management is a great boon to SPAs. In particular, you don't have to think about persisting state between navigations; it just happens automatically. In an MPA, you would have to serialize this state into some persistent format (LocalStorage, IndexedDB, etc.) when the page unloads, and then rehydrate on page load. On the other hand, the fact that the state never gets blown away is exactly what leads to memory leaks - a problem endemic to SPAs that I've already documented ad nauseam. Plus, the further that the state can veer from a known good initial value, the more likely you are to run into bugs, which is why a misbehaving SPA often just needs a good refresh. Interestingly, though, it's not always the case that an MPA navigation lands on a fresh state. As mentioned in a previous post, the back-forward cache (now implemented in all browsers) makes this discussion more nuanced. Cache contents A quick refresher: in modern browsers, the back-forward cache (or BF cache for short) keeps a cache of the previous and next page when navigating between pages on the same origin. This vastly reduces load times when navigating back and forth through standard MPA pages. But how exactly does this cache work? Even an MPA page can be very dynamic. What if the page has been dynamically modified, or the DOM state has changed, or the JavaScript state has changed? What does the browser actually cache? To test this out, I wrote a simple test page. On this page, you can set state in a variety of ways: DOM state, JavaScript heap state, scroll state. Then you can click a link to another page, press the back button, and see what the browser remembers. As it turns out, the browser remembers a lot. I tested this in various browsers (Chrome/Firefox/Safari on desktop, Chrome/Firefox on Android, Safari on iOS), and saw the same result in all of them: the full page state is maintained after pressing the back button. Here is a video demonstration: Note that the scroll positions on both the main document and the subscroller are preserved. More impressively, JavaScript state that isn't even represented in the DOM (here, the number of times a button was clicked) is also preserved. Now, to be clear: this doesn't solve the problem of maintaining state in normal forward navigations. Everything I said above about MPAs needing to serialize their state would apply to any navigation that isn't cached. Also, this behavior may vary subtly between browsers, and their heuristics might not work for your website. But it is impressive that the browser gives you so much out-of-the-box. Conclusion There are dozens of reasons to reach for an SPA technology, MPA technology, or some blend of the two. Everything depends on the needs and constraints of what you're trying to build. In these past few posts, I've tried to shed light on some interesting changes to MPAs that have happened under our very feet, while we might not have noticed. These changes are important, and may shift the calculus when trying to decide between an SPA or MPA architecture. To be fair, though, SPAs haven't stopped moving either: experimental browser APIs like the Navigation API are even trying to solve longstanding problems of focus and scroll management. And of course, frameworks are still innovating on both SPAs and MPAs. The fact that SPAs neatly simplify so many aspects of application development - keeping state in one place, on the main thread, persistent across navigations - is one of their greatest strengths as well as a predictable wellspring of problems. Performance and accessibility wonks can continue harping on the problems of SPAs, but at the end of the day, if developers find it easier to code an SPA than the equivalent MPA, then SPAs will continue to be built. Making MPAs more capable is only one way of solving the problem: approaching things from the other end - such as improved tooling, guidance, and education for SPA developers - can also work toward the same end goal. As tempting as it may be to pronounce one set of tools as dead and another as ascendant, it's important to remain humble and remember that everyone is working under a different set of constraints, and we all have a different take on web development. For that reason, I've come around to the conclusion that SPAs are not going anywhere anytime soon, and will probably remain a compelling development paradigm for as long as the web is around. Some developers will choose one perspective, some will choose another, and the big, beautiful elephant will continue lumbering forward. Related Leave a Reply Cancel reply Enter your comment here... [ ] Fill in your details below or click an icon to log in: * * * * Gravatar Email (required) (Address never made public) [ ] Name (required) [ ] Website [ ] WordPress.com Logo You are commenting using your WordPress.com account. ( Log Out / Change ) Twitter picture You are commenting using your Twitter account. ( Log Out / Change ) Facebook photo You are commenting using your Facebook account. ( Log Out / Change ) Cancel Connecting to %s [ ] Notify me of new comments via email. [ ] Notify me of new posts via email. [Post Comment] [ ] [ ] [ ] [ ] [ ] [ ] [ ] D[ ] This site uses Akismet to reduce spam. Learn how your comment data is processed. Recent Posts * State is hard: why SPAs will persist * More thoughts on SPAs * The balance has shifted away from SPAs * The struggle of using native emoji on the web * Five years of quitting Twitter About Me Nolan Lawson - photo of me flashing a peace sign Hi, I'm Nolan. I'm a web developer living in Seattle and working for Salesforce. Opinions expressed in this blog are mine and frequently wrong. Archives * May 2022 (3) * April 2022 (1) * February 2022 (1) * January 2022 (1) * December 2021 (3) * September 2021 (1) * August 2021 (6) * February 2021 (2) * January 2021 (2) * December 2020 (1) * July 2020 (1) * June 2020 (1) * May 2020 (2) * February 2020 (1) * December 2019 (1) * November 2019 (1) * September 2019 (1) * August 2019 (2) * June 2019 (4) * May 2019 (3) * February 2019 (2) * January 2019 (1) * November 2018 (1) * September 2018 (5) * August 2018 (1) * May 2018 (1) * April 2018 (1) * March 2018 (1) * January 2018 (1) * December 2017 (1) * November 2017 (2) * October 2017 (1) * August 2017 (1) * May 2017 (1) * March 2017 (1) * January 2017 (1) * October 2016 (1) * August 2016 (1) * June 2016 (1) * April 2016 (1) * February 2016 (2) * December 2015 (1) * October 2015 (1) * September 2015 (1) * July 2015 (1) * June 2015 (2) * October 2014 (1) * September 2014 (1) * April 2014 (1) * March 2014 (1) * December 2013 (2) * November 2013 (3) * August 2013 (1) * May 2013 (3) * January 2013 (1) * December 2012 (1) * November 2012 (1) * October 2012 (1) * September 2012 (3) * June 2012 (2) * March 2012 (3) * February 2012 (1) * January 2012 (1) * November 2011 (1) * August 2011 (1) * July 2011 (1) * June 2011 (3) * May 2011 (2) * April 2011 (4) * March 2011 (1) Tags accessibility alogcat android android market apple app tracker blobs boost bootstrap browsers bug reports catlog chord reader code contacts continuous integration copyright couch apps couchdb couchdroid developers development grails html5 indexeddb information retrieval japanese name converter javascript jenkins keepscore listview localstorage logcat logviewer lucene mobile web modules nginx nlp node nodejs npm offline-first open source passwords performance pokedroid pouchdb pouchdroid query expansion relatedness calculator relatedness coefficient s3 safari satire sectioned listview security semver social media socket.io software development solr spas supersaiyanscrollview synonyms twitter ui design ultimate crossword w3c webapp webapps web platform web sockets websql web workers Links * Mastodon * GitHub * npm * Keybase Blog at WordPress.com. * Follow Following + [05fe10] Read the Tea Leaves Join 1,171 other followers [ ] Sign me up + Already have a WordPress.com account? Log in now. * + [05fe10] Read the Tea Leaves + Customize + Follow Following + Sign up + Log in + Copy shortlink + Report this content + View post in Reader + Manage subscriptions + Collapse this bar [b]