Routing on the Web Without a Library

The browser already gives us the primitives needed to change a page's URL without triggering a full refresh. The History.pushState() and History.replaceState() methods update the address bar, and because JavaScript can manipulate the DOM at will, that's technically all a single-page app needs to swap "pages." The missing piece is the routing logic—the matching of a URL against patterns to decide what renders.

That logic is usually where developers reach for a dedicated router. In the React ecosystem, for example, React Router lets routes be defined inside JSX. A <Switch> component walks its child <Route> elements and renders the first one that matches the current URL. The API also supports dynamic segments like :id, which behave as wildcards whose captured values are passed down to components for queries and similar work.

What those libraries really encode is a shared syntax for URL patterns—something that looks a bit like regular expressions but adds domain-specific tokens for matching path segments. There hasn't been a standard for this at the platform level, so each library has had to implement its own.

That gap is what the URLPattern proposal aims to close. As Google's Jeff Posnick explains, routing is fundamental to almost every web app: a URL comes in, some pattern matching runs, and content is rendered based on the result. JavaScript developers have converged on a common pattern syntax, but it lives above the native platform. URLPattern brings that functionality closer to the browser.

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

You define a pattern and then test it against a target URL—typically the one currently in the address bar:

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'

The payoff is twofold. Small applications could potentially handle routing with the native API, skipping routing libraries entirely. For more complex apps that still reach for dependencies, those libraries could now be built on top of a standard primitive, which in turn means less code shipped over the wire.

Note that URLPattern is not a settled standard. It's an evolving proposal, so the safest path is to read the project documentation and experiment through an official polyfill if you want to try it in production today.

URLPattern isn't the only platform effort pointed at SPA pain points. Shared Element Transitions, an even more ambitious API, is also picking up momentum again—with the goal of animating UI elements seamlessly as users navigate between app states.