URLPattern API in Practice: Native Browser Route Matching and Pattern Parsing

技术架构(Updated Jun 16, 2026)

URLPattern API Overview

URLPattern is a browser-native URL pattern matching interface inspired by Express routing syntax, but more powerful—supporting independent pattern matching across protocol, hostname, port, pathname, search, and hash components.

const pattern = new URLPattern({ pathname: '/api/users/:id' });
const result = pattern.exec('https://example.com/api/users/42');
console.log(result.pathname.groups.id);

Pattern Syntax Deep Dive

Basic Syntax: Wildcards and Named Groups

Syntax Meaning Example Match
* Wildcard, matches any string /api/* /api/anything
:name Named capture group /users/:id /users/42{id: "42"}
:name+ Named group matching multiple segments /files/:path+ /files/a/b/c{path: "a/b/c"}
(.*) Regex group /old/(.*) /old/page{0: "page"}

Regex-Constrained Named Groups

const pattern = new URLPattern({
  pathname: '/products/:id(\\d+)'
});

pattern.test('https://shop.com/products/123');
pattern.test('https://shop.com/products/abc');

Full URL Component Patterns

const apiPattern = new URLPattern({
  protocol: 'https',
  hostname: ':sub.example.com',
  pathname: '/v:version/:resource/:id?',
  search: 'sort=:sort&*'
});

const match = apiPattern.exec({
  protocol: 'https',
  hostname: 'api.example.com',
  pathname: '/v2/users/42',
  search: 'sort=name&page=1'
});

if (match) {
  console.log(match.hostname.groups.sub);
  console.log(match.pathname.groups.version);
  console.log(match.pathname.groups.resource);
  console.log(match.pathname.groups.id);
  console.log(match.search.groups.sort);
}

Service Worker Route Interception

Combining URLPattern with Service Workers is the core scenario for a browser-native routing layer.

URLPattern-Based SW Route Dispatch

self.addEventListener('fetch', (event) => {
  const routes = [
    { pattern: new URLPattern({ pathname: '/api/*' }), handler: handleApi },
    { pattern: new URLPattern({ pathname: '/static/*' }), handler: handleStatic },
    { pattern: new URLPattern({ pathname: '/pages/:slug' }), handler: handlePage },
  ];

  for (const { pattern, handler } of routes) {
    const result = pattern.exec(event.request.url);
    if (result) {
      event.respondWith(handler(event, result));
      return;
    }
  }
});

async function handleApi(event, match) {
  const cache = await caches.open('api-cache');
  const cached = await cache.match(event.request);
  if (cached) return cached;

  const response = await fetch(event.request);
  await cache.put(event.request, response.clone());
  return response;
}

Using Chrome Route API (Experimental)

if ('routes' in self.registration) {
  self.registration.routes.add(
    new URLPattern({ pathname: '/api/*' }),
    new CacheFirstStrategy({ cacheName: 'api-cache' })
  );
}

Client-Side Route Matching

SPA Router Implementation

class Router {
  constructor() {
    this.routes = [];
    window.addEventListener('popstate', () => this.resolve());
  }

  add(pattern, handler) {
    this.routes.push({
      pattern: new URLPattern({ pathname: pattern }),
      handler
    });
    return this;
  }

  resolve(pathname = location.pathname) {
    for (const { pattern, handler } of this.routes) {
      const result = pattern.exec({ pathname });
      if (result) {
        handler(result.pathname.groups);
        return;
      }
    }
    console.warn(`No route matched: ${pathname}`);
  }

  navigate(path) {
    history.pushState(null, '', path);
    this.resolve(path);
  }
}

const router = new Router();
router
  .add('/dashboard', (params) => renderDashboard())
  .add('/users/:id', (params) => renderUser(params.id))
  .add('/posts/:slug/comments/:commentId?', (params) => renderComment(params))
  .add('/*', () => renderNotFound());

router.resolve();

Pattern Matching vs Traditional Approaches

Feature URLPattern API RegExp path-to-regexp Express
Browser native
URL component split ✅ proto/host/path/query Partial
Named capture groups ❌ (pre-ES2025)
Regex constraints :id(\\d+)
Type-safe results ✅ groups object
Performance Native C++ impl JS engine opt JS parse JS parse

Practical Pattern Collection

const patterns = {
  apiV1: new URLPattern({ pathname: '/api/v1/:resource/:id?' }),
  apiV2: new URLPattern({ pathname: '/api/v2/:resource/:id?' }),
  userAssets: new URLPattern({ pathname: '/users/:userId/:type(img|doc|pdf)/*' }),
  localePage: new URLPattern({ pathname: '/:locale(en|zh|ja)/:page*' }),
  searchWithFilter: new URLPattern({ pathname: '/search', search: 'q=:query&filter=:filter?' }),
};

function matchRoute(url) {
  for (const [name, pattern] of Object.entries(patterns)) {
    const result = pattern.exec(url);
    if (result) return { name, groups: result.pathname.groups, search: result.search.groups };
  }
  return null;
}

Browser Compatibility and Polyfill

Browser Support Notes
Chrome 95+ Full support
Edge 95+ Full support
Firefox ⚠️ Requires polyfill
Safari 17+ Full support
npm install urlpattern-polyfill
import { URLPattern } from 'urlpattern-polyfill';
if (!globalThis.URLPattern) {
  globalThis.URLPattern = URLPattern;
}

Best Practices Summary

  1. Prefer named groups over index access for readability and maintainability
  2. Apply regex constraints to dynamic segments to prevent invalid matches from entering handler logic
  3. Order SW routes by priority—API routes before wildcard fallbacks
  4. Leverage URL component matching—protocol and hostname patterns can lock down secure origins
  5. Progressive enhancement: detect API availability, fall back to RegExp when unavailable

URLPattern API liberates route matching from framework dependencies, making it part of the browser's infrastructure. Combined with Service Workers, it provides a native routing layer for offline-first architectures.

Try these browser-local tools — no sign-up required →

#URLPattern#路由匹配#Service Worker#Web API#模式匹配