Most applications will use static routes like /about
and dynamic routes like /users/:userId
like we just saw in Dynamic Route Matching, but Vue Router has much more to offer!
:::tip
For the sake of simplicity, all route records are omitting the component
property to focus on the path
value.
:::
When defining a param like :userId
, we internally use the following regex ([^/]+)
(at least one character that isn't a slash /
) to extract params from URLs. This works well unless you need to differentiate two routes based on the param content. Imagine two routes /:orderId
and /:productName
, both would match the exact same URLs, so we need a way to differentiate them. The easiest way would be to add a static section to the path that differentiates them:
const routes = [
// matches /o/3549
{ path: '/o/:orderId' },
// matches /p/books
{ path: '/p/:productName' },
]
But in some scenarios, we don't want to add that static section /o
or /p
. However, orderId
is always a number while productName
can be anything so we can specify a custom regex for a param in parentheses:
const routes = [
// /:orderId -> matches only numbers
{ path: '/:orderId(\\d+)' },
// /:productName -> matches anything else
{ path: '/:productName' },
]
Now, going to /25
will match /:orderId
while going to anything else will match /:productName
. The order of the routes
array doesn't even matter!
:::tip
Make sure to escape backslashes (\
) like we did with \d
(becomes \\d
) to actually pass the backslash character in a string in JavaScript.
:::
If you need to match routes with multiple sections like /first/second/third
, you should mark a param as repeatable with *
(0 or more) and +
(1 or more):
const routes = [
// /:chapters -> matches /one, /one/two, /one/two/three, etc
{ path: '/:chapters+' },
// /:chapters -> matches /, /one, /one/two, /one/two/three, etc
{ path: '/:chapters*' },
]
This will give you an array of params instead of a string and will also require you to pass an array when using named routes:
// given { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// produces /
router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
// produces /a/b
// given { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// throws an Error because `chapters` is empty
These can also be combined with a custom regex by adding them after the closing parentheses:
const routes = [
// only match numbers
// matches /1, /1/2, etc
{ path: '/:chapters(\\d+)+' },
// matches /, /1, /1/2, etc
{ path: '/:chapters(\\d+)*' },
]
By default, all routes are case-insensitive and match routes with or without a trailing slash. e.g. a route /users
matches /users
, /users/
, and even /Users/
. This behavior can be configured with the strict
and sensitive
options, they can be set both at a router and route level:
const router = createRouter({
history: createWebHistory(),
routes: [
// will match /users/posva but not:
// - /users/posva/ because of strict: true
// - /Users/posva because of sensitive: true
{ path: '/users/:id', sensitive: true },
// will match /users, /Users, and /users/42 but not /users/ or /users/42/
{ path: '/users/:id?' },
],
strict: true, // applies to all routes
})
You can also mark a parameter as optional by using the ?
modifier (0 or 1):
const routes = [
// will match /users and /users/posva
{ path: '/users/:userId?' },
// will match /users and /users/42
{ path: '/users/:userId(\\d+)?' },
]
Note that *
technically also marks a parameter as optional but ?
parameters cannot be repeated.
If the route segment contains more than just an optional parameter, it won't match a path without the trailing slash. For example:
/users/:uid?-:name?
won't match/users
, only/users/-
or even/users/-/
/users/:uid(\\d+)?:name?
won't match/users
, only/users/
,/users/2
,/users/2/
, etc
You can play around with the matching syntax in the playground
If you need to dig how your routes are transformed into a regex to understand why a route isn't being matched or, to report a bug, you can use the path ranker tool. It supports sharing your routes through the URL.
When using custom regex, make sure to avoid using slow regex patterns. For example, using .*
will match any character and can lead to serious performance issues if it's combined with a repeatable modifier *
or +
and anything after it:
const routes = [
// This creates a very slow regex because of the greedy `.*` followed by `*` and a static string
{ path: '/:pathMatch(.*)*/something-at-the-end' },
]
In practice, use these match everything params only in the very end of the URL. If you need them in the middle of the path, do not make them repeatable:
const routes = [
// This is fine because the `.*` is at the end
{ path: '/:pathMatch(.*)/something-at-the-end' },
]
This matches the same routes but without an array of params and it's much faster.