In this blog series, I’ll use the term multi-page behaviours to refer to the set of behaviours you expect from a traditional Multi-Page Application (MPA)—regardless whether or not they’re implemented like a traditional MPA.
First, what is an MPA?
A Multi-Page Application (MPA) is usually defined as web application that modifies content using full page loads, where the browser sends requests for completely new web pages to the web server, and the web server responds using Server-Side Rendering (SSR) to generate full web pages and return them to the browser for display. Think of a traditional web application that runs on the server and generates entirely new HTML pages when you click a link. MPAs are very well understood and more than 25 years old now.
One of the problems with this traditional definition of an MPA is that it conflates two different concerns: it implies a set of behaviours (which affect users) while it also describes a particular implementation (which matters more to developers). However in the modern web, behaviour and implementation have become increasingly decoupled.
- Behaviours: Does the application have multiple logical “pages”? Is your current location shareable? Does the URL change as you navigate the application? Does the application support deep links and bookmarking? Can you right-click a link and open it in a new browser tab? Can you use the application in multiple tabs? Do the browser’s Back and Forward buttons retrace your navigational steps?
- Implementation: Does the application use Server-Side Rendering (SSR), Client-Side Rendering (CSR), or some kind of hybrid? Does the browser communicate with the server using synchronous HTTP requests or asynchronous API calls? Does it do something funky with WebSockets? Under what circumstances do page reloads occur? Does it use the HTML5 History API?
Until about ten years ago this conflation of behaviour and implementation made sense, since they were tightly coupled. If you wanted to provide all the behaviours expected of an MPA, you had to build a traditional Multi-Page Application running on a server using Server-Side Rendering (SSR). Conversely, if you wanted your application to run in the browser using Client-Side Rendering (CSR), you had to forgo many of the those behaviours—which had major implications for the user experience.
Over the last ten years, this has changed. Pretty much any type of web application can support multi-page behaviours, at least in principle. This includes applications that run entirely the browser, as well as various kinds of hybrid applications. In this blog series, I’ll look at some modern web application designs and examine how they support multi-page behaviours.
What happens if we set aside the implementational aspects of MPAs, and spell out only the implied behaviours? Here is my initial attempt at doing this, which I might revise in response to new ideas or feedback.
|Shareable||You can “share” your current location using the browser’s native Share button, or by copying the current URL from the address bar. This allows you to obtain a link to your current location within the application, for use in emails, documents, messages, and so on. You can also use your browser’s native Bookmarking functionality to store your current location as a bookmark or favourite.1|
|Deep linkable||When previously shared or bookmarked links are opened, they take you back to their original locations. This applies even if the link is opened by a different user (assuming it refers to a resource they have permission to).|
|Refreshable||You can use the browser’s Reload button to refresh the data displayed at your current location. While any unsaved data stored locally may be lost, your location within the application stays the same, and the page is regenerated using the latest externally-sourced data where available.|
|History traversable||You can use the browser’s Back and Forward buttons, or select a location from your browser’s history, to retrace your navigational steps within the application. This does not mean that Back acts like an “undo” button and reverts changes to state—rather, it takes you back to your previous navigational location within the application.|
|Link operable||Hyperlinks within the application are fully operable. This means they provide a rich set of standard interactions besides primary-clicking, such as the ability to right-click and open them in a different browser tab or window, or copy their destination URLs. This applies to both text and image hyperlinks. It does not apply to elements such as buttons that do not act as hyperlinks.|
|Multi-tabbable||You can open the same application in multiple browser tabs or windows at the same time, and switch between them at will—for example to compare different items or records, or to refer to information in one page while completing a task in another page. If the application is authenticated, then signing in to one tab signs you in to all tabs, including those you open after signing in. Using the application in one tab does not terminate your session in the others.|
Later in this blog series, I’ll describe various design patterns for web applications, and I’ll evaluate each pattern against these behaviours—so I’ll refer back to this list and use it as a kind of benchmark.
Before that, here are some key observations about multi-page behaviours.
First class user interactions
When a web application truly supports multi-page behaviours, it treats them as first-class user interactions—not as an afterthought. For example, if a user wants to right-click a link and open it in a new browser tab, that’s up to them. Opening links in new tabs is a normal part of using such applications, so they need to support it smoothly and efficiently.
A single user action might exercise multiple of the multi-page behaviours listed above.
Consider right-clicking a link and opening it in a new browser tab, for example. To support this action, an application must be link operable (to allow the user to right-click a link and select “Open Link in New Tab”), deep linkable (to successfully open the target URL’s new page in the new browser tab), and multi-tabbable (if it’s an authenticated application, to ensure the user is signed in to both tabs).
For this reason, I’ve defined the multi-page behaviours so they are granular and composable, and can be combined in various ways to describe common user actions.2
Should every web application support multi-page behaviours?
Not at all! There are many reasons why it might not be desirable or feasible for a particular web application to support multi-page behaviours.
It’s difficult to come up with an exhaustive set of criteria, but a few obvious examples spring to mind. Games, 3D modelling, complex client-side applications that take a long time to “initialize”, and the Google Santa Tracker, for example. These are applications types that probably don’t need multi-page behaviours, or might not work well with them. In certain types of complex web applications, there are subtleties and edge cases relating to state management which developers need to consider, which can make supporting multi-page behaviours very challenging or infeasible.
It’s worth noting that some web applications support multi-page behaviours at some level of granularity, but not others. For example, specific pages might be accessible and shareable using multi-page behaviours—but once opened, those individual pages exhibit single-page behaviour using asynchronous content updates. That is perfectly fine if it makes sense in the context of the application.
Sometimes it makes sense to support some multi-page behaviours and not others. For example, you want to make lots of small changes to the state of the application, and reflect those in the URL—but you don’t want the user to have to press Back hundreds of time to get out. In other words, you want those changes to be shareable, but not history traversible.
The web’s usability deficit
It’s probably fair to say that the web generally suffers from a usability deficit with respect to multi-page behaviours—although things may be improving. This usability deficit relates to the set of web applications that ought to support multi-page behaviours from a usability perspective—yet for whatever reason, don’t. There is a huge number of them, especially if you include applications within intranets and enterprises as well as on the public web.
The usability deficit does not include web applications that have opted to avoid multi-page behaviours for some justifiable reason; they’re OK.
Where is content rendered?
When it comes to implementation, web applications are often described based on where they render content. Traditionally, web content can be rendered in three places, summarized below.
|Static Site Generation
|Content is rendered offline, usually in some kind of build environment, and deposited to a web server or CDN as static content. At runtime, the static content is sent to the browser for display.|
|Content is rendered dynamically at runtime by a web or application server, and then sent to the browser for display.|
|Content is rendered dynamically at runtime by client-side code running in the browser using the Document Object Model (DOM) interface.|
You can think of these as the “holy trinity” of web application design. Web applications can use SSG, SSR and CSR individually or in combination. All three techniques allow multi-page behaviours to be fully supported.
One important thing to note is that when a web site uses pure SSG or pure SSR (or a mixture of the two), then the multi-page behaviours generally work by default.3 When a web server sends content in response to a full page load, whether it’s statically or dynamically generated, it automatically produces a separate logical web page, and you don’t need to do anything special to support multi-page behaviours. In fact some types of “special” will actually break them. If you do nothing, they mostly Just Work.
With pure CSR, that is not the case. If your web application runs entirely in the browser, then most multi-page behaviours are broken by default. But with a bit of extra work, you can make them all work—albeit with trade-offs in some cases.
For this reason I’ll focus on pure CSR applications for most of this blog series. But I’ll also touch on hybrid applications that have become popular in the last few years, which combine SSG, SSR and CSR in new and creative ways.
Many popular web frameworks can be used to build on these foundations—but I won’t talk much about web frameworks. There are several reasons for this. First, web frameworks provide layers of abstraction over what’s happening underneath, and the foundations need to be in place before you deal with abstractions. Second, web frameworks are evolving at tremendous pace, and it’s hard to keep up! If you want to know how to support multi-page behaviours using your favourite web framework, there are other people doing a much better job of that than me.
With those preliminaries out of the way, let’s begin!
In the second post I’ll describe a key browser feature introduced in 2011 that helps CSR applications support multi-page behaviours.
In an earlier version of this blog post I had a bookmarkable behaviour—but then I decided it’s redundant, since bookmarkable is a special case of shareable: when you bookmark a page, you’re effectively “sharing” it with your future self. ↩
Multi-page behaviours can also be used to describe missing features. For example, some web applications support deep links, but those deep links cannot be generated using the browser’s native Share button or by copying the URL in the address bar. Instead, they must be generated using a custom “Permalink” button in the browsers’s content area. Such applications are deep linkable but not shareable in the standard way. ↩
Blogs like the one you’re reading now are examples of web sites generated entirely using SSG. ↩