How I Implemented my own SPA Routing System in Vanilla JS

Bryan Manuele (Fermi Dirak)
4 min readApr 25, 2018

--

You want to make your web-site fast. Blazing fast. But you have an issue — every time a user navigates to a new page, the browser has to make a request to the server for that page’s documents. This in turn causes the website to reload, which leaves your user staring at a blank screen for a split second before the requested content is finally rendered onto the page.

Modern web applications often opt to use a single-page architecture (SPA) with routing to reduce subsequent page load times. Plenty of modern frameworks (Angular, React, Vue, etc.) encourage SPAs with routing because of the benefits they bring to the user experience. A router solves the issue of having to make requests to the server each time users request new content by having all the website’s content loaded on initial load. It then dynamically displays the correct content onto the page based on the URL’s pathname.

A router dynamically loads the page’s content based on the url pathname

The web app parses the pathname in the url and displays the content associated with that pathname. Because our content is stored in memory instead of on the server, swapping between pages within our web apps, page swap times become virtually unnoticeable.

Building the Router

Now that we know how SPA apps and routing work in theory, let’s build one to better understand how it fundamentally works and understand what’s going on behind the scenes.

When a user navigates to a route’s path, our client will serve its associated template page. To achieve this, we’ll need a dictionary of routes to templates:

routes = {
'/': homePage,
'/portfolio': portfolioPage,
'/resume': resumePage,
'/contact': contactPage,
};

and each route’s corresponding template page:

a sample template page. In a non-example app, a templating library such as Handlebars or EJS should be used instead of HTML in JS

When ever a user navigates to a new page (via the navbar or a link), we need to set the content-div’s content to be the appropriate template:

let contentDiv = document.getElementById('content');
contentDiv.innerHTML = routes[window.location.pathname];

In order for routing to work on your local dev environment, you’ll need to serve your website from a server instead of viewing it statically from your computer’s file structure. The app hosted and served from a server because we need to override the server’s file serving functionality to always serve our app, /index.html. To do this, run:

live-server -port=3000 -entry-file=’./index.html’

You can then access your app by pointing your browser to localhost:3000

The Web History API

By now, we’ve successfully implemented a single page architecture with routing. There’s just one caveat: our app currently doesn’t keep track of the user’s browsing history within the app. To fix this, we’ll need to use the browser’s Web History API to manually add the current page’s url (url origin + url pathname) to the user’s navigation history. Below is a function that demonstrates how this would be done. It’s called whenever a user navigates to path pathName.

let onNavItemClick = (pathName) => {
window.history.pushState(
{},
pathName,
window.location.origin + pathName
);
contentDiv.innerHTML = routes[pathName];
}

In this function, we’re pushing the url window.location.origin + pathName to the window’s history. Now each time a user navigates to a different page, it will properly be added to the Browser’s history.

Finally, we need to ensure that the user is displayed the right content when they navigate back in their browsing history. To do this, override the window’s onpopstate function, which is called whenever the user navigates back in history.

window.onpopstate = () => {
contentDiv.innerHTML = routes[window.location.pathname];
}

With that, we have a fully fledged Single Page App with dynamic routing!

If you want to see an example of this in action, check out the source code to my portfolio website fermidirak.github.io on Github. It’s masochistically built completely in vanilla javascript and demonstrates how to implement modern web app features completely from scratch. One of those features is the single page architecture with routing. If you’re interested in seeing vanilla SPA routing in action, check out the source code on Github.

Recap

The Single Page Application model dramatically reduces page load times when navigating within your web app. This architecture is incredibly practical for smaller web applications, as the tradeoff of a slightly longer initial load for incredibly fast navigation is proportional to the web app’s size. By seeing how a simple routing library would be implemented from scratch, one can better understand why SPAs are used everywhere and what is actually going on behind the scenes when you use one :)

If you want to talk feel free to reach out to me. I’m based in SF, and I’m on Linkedin and also on Github. Thank you so much for reading!

--

--

Bryan Manuele (Fermi Dirak)

I'm a Full Stack Engineer at Flexport. I sometimes writes about things that I'm sometimes interested in :)