Native JavaScript Routing?

Avatar of Chris Coyier
Chris Coyier on

We can update the URL in JavaScript. We’ve got the APIs pushState and replaceState:

// Adds to browser history
history.pushState({}, "About Page", "/about");

// Doesn't
history.replaceState({}, "About Page", "/about");

JavaScript is also capable of replacing any content in the DOM.

// Hardcore
document.body.innerHTML = `
  <div>New body who dis.</div>
`;

So with those powers combined, we can build a website where we navigate to different “pages” but the browser never refreshes. That’s literally what “Single Page App” (SPA) means.

But routing can get a bit complicated. We’re really on our own implementing it outside these somewhat low-level APIs. I’m most familiar with reaching for something like React Router, which allows the expression of routes in JSX. Something like this:

<Router>
  <Switch>
    <Route path="/about">
      <About />
    </Route>
    <Route path="/users">
      <Users />
    </Route>
    <Route path="/user/:id">
      <User id={id} />
    </Route>
    <Route path="/">
      <Home />
    </Route>
  </Switch>
</Router>

The docs describe this bit like:

A <Switch> looks through its children <Route> and renders the first one that matches the current URL.

So it’s a little bit like a RegEx matcher with API niceties, like the ability to make a “token” with something like :id that acts as a wildcard you can pass to components to use in queries and such.

This is work! Hence the reason we have libraries to help us. But it looks like the web platform is doing what it does best and stepping in to help where it can. Over on the Google webdev blog, this is explained largely the same way:

Routing is a key piece of every web application. At its heart, routing involves taking a URL, applying some pattern matching or other app-specific logic to it, and then, usually, displaying web content based on the result. Routing might be implemented in a number of ways: it’s sometimes code running on a server that maps a path to files on disk, or logic in a single-page app that waits for changes to the current location and creates a corresponding piece of DOM to display.

While there is no one definitive standard, web developers have gravitated towards a common syntax for expressing URL routing patterns that share a lot in common with regular expressions, but with some domain-specific additions like tokens for matching path segments.

Jeff Posnick, “URLPattern brings routing to the web platform”

New tech!

const p = new URLPattern({
  pathname: '/foo/:image.jpg',
  baseURL: 'https://example.com',
});

We can set up a pattern like that, and then run tests against it by shooting it a URL (probably the currently navigated-to one):

let result = p.test('https://example.com/foo/cat.jpg');
// true

result = p.exec('https://imagecdn1.example.com/foo/cat.jpg');
// result.hostname.groups.subdomain will be 'imagecdn1'
// result.pathname.groups[0] will be 'foo', corresponding to *
// result.pathname.groups.image will be 'cat'

I would think the point of all this is perhaps being able to build routing into SPAs without having to reach for libraries, making for lighter/faster websites. Or that the libraries that help us with routing can leverage it, making the libraries smaller, and ultimately websites that are lighter and faster.

This is not solid tech yet, so probably best to just read the blog post to get the gist. And use the polyfill if you want to try it out.

And speaking of the web platform showing love on SPAs lately, check out Shared Element Transitions which seems to be re-gaining momentum.