
Security News
Lovable’s OJ Rewrites Vite’s Dev Server in Rust as AI Lowers the Cost of Forking Open Source
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.
@remix-run/route-pattern
Advanced tools
Type-safe URL matching and href generation for JavaScript. route-pattern supports path variables, wildcards, optionals, search constraints, and full-URL patterns with predictable ranking.
npm i remix
import { createMultiMatcher } from 'remix/route-pattern/match'
let matcher = createMultiMatcher<{ name: string }>()
matcher.add('blog/:slug', { name: 'blog-post' })
matcher.add('api(/v:version)/*path', { name: 'api' })
matcher.add('http(s)://:region.cdn.com/assets/*file.:ext', { name: 'assets' })
let match = matcher.match('https://example.com/blog/v3')
match?.pattern.toString()
// /blog/:slug
match?.params
// { slug: 'v3' }
match?.data
// { name: 'blog-post' }
import { createHref } from 'remix/route-pattern/href'
createHref('blog/:slug', { slug: 'v3' })
// '/blog/v3'
createHref('api(/v:version)/*path', { version: '2', path: 'users/profile' })
// '/api/v2/users/profile'
createHref('http(s)://:region.cdn.com/assets/*file.:ext', {
region: 'us-west',
file: 'images/logo',
ext: 'png',
})
// 'https://us-west.cdn.com/assets/images/logo.png'
| Import | Description |
|---|---|
remix/route-pattern | Parse and stringify patterns. |
remix/route-pattern/href | Generate hrefs for patterns with type safe params. |
remix/route-pattern/match | Match against one pattern with type inference for params, or match against many patterns with deterministic ranking and attached data. |
remix/route-pattern/join | Combine two patterns into one. Override protocol, hostname, port. Join pathnames. Merge search constraints. |
remix/route-pattern/specificity | Rank matches by specificity. |
For in-depth reference, visit the route-pattern API docs
Protocol must be http, https, or http(s):
'https://example.com' // matches https://example.com
'http(s)://example.com' // matches http://example.com, https://example.com
Variables capture dynamic segments using :name:
'users/:id' // matches /users/123
'blog/:year-:month-:day/:slug' // matches /blog/2024-01-15/hello
Wildcards match multi-segment paths using *name:
'files/*path' // matches /files/images/logo.png
'node_modules/*package/dist/index.js' // matches /node_modules/@remix-run/router/dist/index.js
'files/*' // matches any path under /files, but doesn't capture the wildcard value
Optionals make parts optional using ():
'api(/v:version)/users' // matches /api/users, /api/v2/users
'blog/:slug(.html)' // matches /blog/hello, /blog/hello.html
'docs(/guides/:category)' // matches /docs, /docs/guides/routing
'api(/v:major(.:minor))' // matches /api, /api/v2, /api/v2.1
While variables, wilcards, and optionals are most prevalent in pathnames, you can also use them in hostnames:
':tenant.example.com/dashboard' // matches acme.example.com/dashboard
'(www.)example.com/blog/:slug(.html)' // matches example.com/blog/hello, www.example.com/blog/hello.html
'*.example.com/files/*path' // matches cdn.example.com/files/images/logo.png
'(:locale.)example.com/docs(/:section)' // matches en.example.com/docs, en.example.com/docs/guides
Escape characters with \:
'time/12\\:30' // matches /time/12:30
'calculator/2\\*3' // matches /calculator/2*3
'wiki/Mercury_\\(planet\\)' // matches /wiki/Mercury_(planet)
'wiki/AC\\/DC' // matches /wiki/AC%2FDC
Search constraints narrow matches using ?key or ?key=value:
'search?q' // key must be present
'search?q=routing' // requires ?q=routing exactly
Use createMatcher when you have one pattern and want params inferred from that exact pattern.
import { createMatcher } from 'remix/route-pattern/match'
const url: string | URL = /* ... */
let blogMatcher = createMatcher('blog/:slug')
blogMatcher.match(url)?.params
// Type safe params ^? { slug: string } | undefined
let docsMatcher = createMatcher('://(:tenant.)host.com/docs/*path.:ext')
docsMatcher.match(url)?.params
// Type safe params ^? { tenant: string | undefined, path: string, ext: string } | undefined
Use createMultiMatcher when you need to match many patterns and attach your own data to each match.
import { createMultiMatcher } from 'remix/route-pattern/match'
let matcher = createMultiMatcher<string>()
// Any data type you want! 👆
matcher.add('/', 'home')
matcher.add('blog/:slug', 'blog-post')
matcher.add('api(/v:version)/*path', 'api')
matcher.match('https://example.com/blog/v3')
// { params: { slug: 'v3' }, data: 'blog-post' }
matcher.match('https://example.com/api/v2/users/profile')
// { params: { version: '2', path: 'users/profile' }, data: 'api' }
The matched pattern is only known at runtime, so matched params are not inferred when matching with createMultiMatcher.
When multiple patterns match the same URL, route-pattern chooses the most specific match deterministically. Matches are ranked left-to-right, character-by-character:
This is the same ranking used by createMultiMatcher.
For advanced use cases, /specificity provides comparison utilities: lessThan, greaterThan, equal, descending, ascending, compare. For example:
import { createMultiMatcher } from 'remix/route-pattern/match'
import { descending } from 'remix/route-pattern/specificity'
let matcher = createMultiMatcher()
matcher.add('files/*path', null)
matcher.add('files/:name', null)
matcher.add('files/readme.md', null)
let matches = matcher.matchAll('https://example.com/files/readme.md')
matches.sort(descending).map((match) => match.pattern.toString())
// ['/files/readme.md', '/files/:name', '/files/*path']
createHref turns a pattern and params into a URL string. Required variables and wildcards must be provided, while params inside optional groups may be omitted.
import { createHref } from 'remix/route-pattern/href'
createHref('blog/:slug', { slug: 'v3' })
// '/blog/v3'
createHref('api(/v:version)/*path', { path: 'users/profile' })
// '/api/users/profile'
createHref('api(/v:version)/*path', { version: '2', path: 'users/profile' })
// '/api/v2/users/profile'
createHref('http(s)://:region.cdn.com/assets/*file.:ext', {
region: 'us-west',
file: 'images/logo',
ext: 'png',
})
// 'https://us-west.cdn.com/assets/images/logo.png'
createHref('blog/:slug?ref=docs', { slug: 'v3' }, { utm_source: 'newsletter' })
// '/blog/v3?utm_source=newsletter&ref=docs'
Note: optional groups without params are included in the generated href:
createHref('todos(/new)')
// '/todos/new'
createHref('products(.json)')
// '/products.json'
You can explicitly parse and stringify patterns:
import { RoutePattern } from 'remix/route-pattern'
let pattern = RoutePattern.parse('://example.com/blog/:slug')
// ^? RoutePattern
pattern.toString()
// '://example.com/blog/:slug'
pattern.toJSON()
// { hostname: 'example.com', pathname: 'blog/:slug', ... }
All APIs that take a pattern arg accept string or a parsed RoutePattern.
TIP: For high-performance scenarios, you can parse patterns ahead of time to avoid reparsing them on every call.
joinPatterns builds a new pattern from a base pattern.
import { joinPatterns } from 'remix/route-pattern/join'
let user = joinPatterns('users', ':id')
user.toString()
// '/users/:id'
let apiUser = joinPatterns('api(/v:version)', '://remix.run/users/:id')
apiUser.toString()
// '://remix.run/api(/v:version)/users/:id'
/ in between as necessaryBenchmarks live in bench/.
See LICENSE
FAQs
Match and generate URLs with strong typing
The npm package @remix-run/route-pattern receives a total of 487,571 weekly downloads. As such, @remix-run/route-pattern popularity was classified as popular.
We found that @remix-run/route-pattern demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Lovable’s OJ rewrites Vite’s dev server in Rust, reducing memory use and preview times as AI lowers the cost of open source reimplementation.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.