If you build a JavaScript app with more than one page or view, you'll probably need a way to determine which UI or layout to show based on the URL. That's routing.
Single-page apps (or SPAs as they're sometimes called) serve all of the code for an entire multi-UI app from a single index.html
file.
They use JavaScript to handle URL routing with real URLs. For this to work, you need to:
- Configure the server to point all paths on a domain back to the root
index.html
file. For example,todolist.com
andtodolist.com/lists
should both point to the same file. - Suppress the default behavior when someone clicks a link that points to another page in the app.
- Use more JavaScript---
history.pushState()
---to update the URL without triggering a page reload. - Match the URL against a map of routes, and serve the right content based on it.
- If your URL has variable information in it (like a todolist ID, for example), parse that data out of the URL.
- Detect when someone clicks the browser's back button/forward button, and update the URL and UI.
You end up recreating with JavaScript a lot of the features the browser gives you out-of-the-box.
This becomes more code to maintain, more complexity to manage, and more things to break. It makes the whole app more fragile and bug-prone than it has to be.
I'm going to share some alternatives that I prefer.
That said, if using JavaScript to handle routing is something you're interested in, Krasimir Tsonev has written an excellent article on how that works^[http://krasimirtsonev.com/blog/article/A-modern-JavaScript-router-in-100-lines-history-api-pushState-hash-url], and open sourced a routing plugin, Navigo^[https://github.com/krasimir/navigo], based on the article.
If you don't use JavaScript to handle routing, what else can you do?
Let the browser load real HTML files located at the actual URLs. Instead of a single-page app, you build a multi-page app.
Looking at our list-making app, let's say we wanted users to have a settings page where they can choose whether or show items as a bulleted or numbered list, and clear all of their list data.
Create a directory in your app called settings
, and add an index.html
file there (this lets you create pretty URLs like list-maker.com/settings/
instead of having something like list-maker.com/settings.html
).
To render the UI, you include a unique selector in the markup that describes the current view (such as [data-app="lists"]
or [data-app="settings"]
).
In your JavaScript file, you can do something like this to determine which template to use.
// Get the app container
var app = document.querySelector('[data-app]');
// Determine the view/UI
var page = app.getAttribute('data-app');
// Render the correct UI
if (page === 'lists') {
// Render the homepage...
}
if (page === 'settings') {
// Render the settings page...
}
A few reasons:
- Support for the browser's forward and backward buttons are baked in. You don't need to do anything to make that work.
- You don't need to intercept clicks and determine if the clicked link points to an internal link or an external one. You just let them all resolve.
- You don't need to use complex regex patterns or another library to parse the URL and determine which view or UI to render. It's baked into the markup already.
- It's simpler and easier to implement.
A counter argument might be that using JavaScript routing results in faster apps because you avoid a page reload.
That can be true, but if you use front end performance best practices and load static HTML files, I find that page loads using this approach feel nearly instantaneous.
For example, this course site is a JavaScript app built using real URLs with full page reloads. When you click from lesson to lesson, the content loads almost immediately. It feels like a single page app, but it's not.
Hi. Thanks for all the great articles. I would like to build an app as described in this article. Splitting my code into multiple pages and keeping the SPA feel. Do you have a working example of this approach? Would be much appreciated.