Published: Oct 20, 2025
If you've ever built a feature that needs to react to URL changes, you've likely found yourself writing complex regular expressions or pulling in a third-party library for routing. The URL Pattern API, now Baseline Newly available, provides a standardized, powerful, and interoperable solution for this common challenge.
It lets you match URLs against a familiar pattern syntax, making it much less difficult to parse URLs and extract data with methods like .exec() and .test().
const pattern = new URLPattern({ pathname: "/products/:id" });
console.log(pattern.test("https://example.com/products/123")); // true
The functionality of the URL Pattern API is available in the URLPattern
interface. In this post, you'll see how using it can help improve your codebase when you come across some common challenges.
Basic URL pattern matching
Before the availability of the URL pattern API, basic URL pattern matching often meant combining the older URL
interface to parse a URL, then apply a separate—often complex—regular expression to the .pathname
property to test for a match.
const url = new URL(location.href);
const pathRegex = /^\/users\/([a-zA-Z0-9]+)\/?$/;
const isMatch = pathRegex.test(url.pathname);
With URLPattern
, this task now requires less code, and is easier to read:
const pattern = new URLPattern({ pathname: "/users/:id" });
const isMatch = pattern.test(location.href);
Extract dynamic parameters
Another common use case for URLPattern
is when you need to extract dynamic parameters from a URL. In the past, numbered regular expression capture groups were often relied on, which are positional and anonymous. You had to access parameters by their array index (result[1]
, result[2]
), which is fragile when the order of the capture groups changes.
const pathRegex = /^\/books\/([a-z]+)\/(\d+)\/?$/;
const result = pathRegex.exec("/books/classics/12345");
// `result` is an array:
// ["/books/classics/12345", "classics", "12345"]
const category = result ? result[1] : null;
const bookId = result ? result[2] : null;
URLPattern
returns a structured groups object, featuring named parameters. The names directly correspond to the keys in the resulting object, leading to explicit and robust code.
// Pattern to match /books/<category>/<id>
const pattern = new URLPattern({ pathname: "/books/:category/:id" });
const result = pattern.exec("/books/classics/12345");
// `result.pathname.groups` will return:
// { category: "classics", id: "12345" }
const { category, id: bookId } = result.pathname.groups;
Compose multipart matches
The typical way to compose multipart matches for a URL in the past would be to use the URL
constructor to get individual parts (hostname
, pathname
), then run separate checks or regular expressions on each.
const url = new URL(req.url);
if (url.hostname.endsWith(".cdn.com") && url.pathname.startsWith("/images/")) {
// Do something
}
URLPattern
supports this natively, with predictable controls:
const pattern = new URLPattern({ hostname: "*.cdn.com", pathname: "/images/*" });
if (pattern.test(req.url)) {
// Do something
}
Project dependencies
Prior to URLPattern
, processing URL strings was often delegated to a third-party library to the application bundle to handle routing logic robustly.
Now that URLPattern
is Baseline Newly available, you'll have zero dependencies for doing this type of work. The functionality is built into the browser, reducing bundle size, eliminating a dependency to manage, and using a potentially more performant in-browser implementation that works across all major browser engines.
Detailed usage
The usefulness of URLPattern
is in its ability to serve both basic and advanced use cases. You can start with simpler path matching and layer in more powerful features as you need them. Here are some common scenarios.
Match a path and extract parameters
A common use case of URL pattern matching is client-side routing. URLPattern
makes it very clean. You can check if a URL matches a pattern with .test()
, and extract the dynamic parts with .exec()
.
const pattern = new URLPattern({ pathname: "/products/:category/:id" });
// Use .test() for a boolean check
console.log(pattern.test("https://example.com/products/electronics/123")); // → true
console.log(pattern.test("https://example.com/blog")); // → false
// Use .exec() to extract the named groups
const result = pattern.exec("https://example.com/products/electronics/123");
if (result) {
const { category, id } = result.pathname.groups;
console.log(`Loading product ${id} from the ${category} category.`); // → "Loading product 123 from the electronics category."
}
How to match subdomains and more
Unlike many routing libraries, URLPattern
is a full URL matcher. This is perfect for apps that behave differently based on the hostname, like routing API calls to a specific subdomain.
Here, a pattern is created that only matches requests to an api
subdomain and a specific path.
const apiPattern = new URLPattern({
hostname: ":subdomain.myapp.com",
pathname: "/api/v:version/*"
});
const result = apiPattern.exec("https://api.myapp.com/api/v2/users");
if (result) {
const { subdomain, version } = result.hostname.groups;
console.log(`Request to the '${subdomain}' subdomain, API version ${version}.`); // → "Request to the 'api' subdomain, API version 2."
}
Wildcards and regular expressions
When you need more flexibility than named groups alone can provide, URLPattern
supports wildcards (*
) and embedding regular expressions directly into your patterns for better control.
For example, here's a pattern to match any image file within a user's asset folder, while also ensuring the user ID is a number.
// :userId(\\d+) - Matches a named group 'userId', but only if it contains one or more digits.
// * - A wildcard that matches any character until the next separator (like a '.')
const assetPattern = new URLPattern({
pathname: "/users/:userId(\\d+)/assets/*.(jpg|png|gif)"
});
const result1 = assetPattern.exec("/users/123/assets/profile.jpg");
console.log(result1?.pathname.groups.userId); // → "123" (It matches)
const result2 = assetPattern.exec("/users/abc/assets/avatar.png");
console.log(result2); // → null (It fails because 'abc' is not a digit)
A practical example: routing in a service worker
Service workers are a perfect use case for URLPattern
. You can cleanly intercept fetch
requests and apply different caching strategies without messy regular expressions or conditional statements relaying on url.includes()
.
// Inside your service worker file:
const IMAGE_ASSETS = new URLPattern({ pathname: "/images/*" });
const API_CALLS = new URLPattern({ pathname: "/api/*" });
self.addEventListener("fetch", (event) => {
const url = event.request.url;
if (IMAGE_ASSETS.test(url)) {
// Apply a cache-first strategy for images
event.respondWith(cacheFirst(event.request));
} else if (API_CALLS.test(url)) {
// Apply a network-first strategy for API calls
event.respondWith(networkFirst(event.request));
}
});
For years, URL routing in the browser has meant complex regular expressions or third-party libraries. URLPattern
changes that by offering a robust, adaptable, and standardized solution built directly into the browser.
So, the next time you find yourself writing a regular expression to parse a URL, stop and consider URLPattern
. Try refactoring a complex routing module in your code, or use it in your next service worker to handle fetch requests. You might find you can simplify your code and maybe even remove a dependency in the process!
The URL Pattern API is packed with more features than can be covered here. To dive deeper and see a full reference of all its capabilities, check out the comprehensive documentation on MDN Web Docs.