Web Routing: What Is the Best Way to Get From A to B?
In This Article
What is routing?
Before we talk about the different approaches to routing, we need to have an agreed-upon definition. For this article, I'll be focusing on routing using the URL only. Since we are dealing with websites, we are going to ignore the protocol (http, https, etc.) and the domain or IP address (wwt.com,
184.108.40.206) as these fall outside the control of a website and its server. We will also ignore the port (
:80 for http,
:443 for https) as the browser can assume which port in most circumstances.
This leaves us with just the portion of the URL that describe the location of a resource on the server, the query string, and the hash string (e.g.
/my-resource?query=string#hash-string). However, because the hash string is never sent from the browser to the server, we will ignore it in this article.
Multi-page routing is the most common form of routing on the web and originally the only option available when writing websites.
Here's an example: A user navigates to our website and visits the page
/home/page. On that page is a link to
/new/page which the user clicks on. The browser then talks to the server and requests the resource at this new location. When it receives the page, it throws away the home page and renders the new one.
- Allows the client to be as simple as possible (pure HTML and CSS).
- State is entirely contained in server, reducing the chance of desynchronization between the server and client.
- Each navigation requires a full page reload, leading to potential white flashes and duplicated information over the network.
- Server is now fully responsible for rendering pages, meaning heavier workloads for the server.
- Browser caching can more easily get in the way of delivering the most up-to-date information to the user.
A note on accessibility:
Single page routing
The idea behind single-page routing is to keep the web page the user is on alive rather than going back to the server for an entirely new page every time the user clicks on a link. Using the same example as above, when the user clicks on a link to navigate to
- API can be created independent from display logic, allowing for reuse between multiple clients such as web and mobile.
- Website is a collection of static files that can be cached, resulting in only data being transferred over the network.
- Routing behavior can be delegated entirely to the client side, such as switching between a viewing and editing interface with no need for the network.
- Scripts can easily become bloated and heavy, especially for mobile devices.
- Must explicitly handle offline and error states in the browser to avoid user confusion.
Generalized single page routing
There is not an established name for this typing of routing, but it can be considered a type of single page routing. Examples of this type of routing are Turbo, Livewire, or HTMX.
While not exclusive to this version of single-page routing, another common efficiency included is to communicate over a Web Socket. Without getting too much into the details, this saves time and data by not creating a new request every time an update is needed and instead uses the socket to stream requests and responses.
- No business logic in the browser.
- Still provides a more instantaneous feel than multi-page app.
- Can usually be used immediately on existing multi-page apps without any server changes needed.
- Still need to handle offline and error states in the browser sometimes.
- For HTML fragments, requires a different pattern of thinking or organizing your code.
- Naive replacement of HTML can cause issues for accessibility and user input, such as wiping out the content of a text field or loss of focus due to being a truly new element. Addressing this requires higher complexity.
Once again there is not an established name for this type of routing. While technically this can be considered a form of single-page routing, it sits somewhere between a generalized approach and an app-specific approach. There is a lack of real world examples because it is such a difficult problem to solve without some form of compromise. The closest examples I can find are Meteor (which is too specialized), Remix , Qwik (which are both very new) and Marko (which has its own syntax). React itself is aiming to provide an approach through Server Components which Next is starting to use.
- Single code base for both the server and the client.
- Smaller payloads than multi-page and most generalized single-page routing solutions.
- Better HTML fragment replacement.
- If already developing in React, should be a relatively cheap and easy upgrade.
- Mental model complexity and complexity in general.
- Coupling your backend and frontend together means your choice of technology is all or nothing.
Any of the above approaches to routing can be made to work for your use case, but there are always trade-offs with whichever one you choose.
- Shared routing allows you to write a single code base for full single page routing and multi page routing, but locks you into a particular technology and has a complex mental model.
I doubt that any of these approaches will ever dominate or disappear from the market soon, but shared routing does seem to be a major goal for the future for frameworks like React. If performance, accessibility, and developer experience continue to improve, it may become the new default approach to creating web applications in the no-so-distant future.