29

I'm using Vue Router with Vue 3 and am trying to add a catch-all route to redirect the user if they try and access an invalid URL. When I try and use the wildcard (*), i get the following error logged to the console:

Uncaught Error: A non-empty path must start with "/"
    at tokenizePath (vue-router.esm.js?8c4f:975)
    at createRouteRecordMatcher (vue-router.esm.js?8c4f:1106)
    at addRoute (vue-router.esm.js?8c4f:1190)
    at eval (vue-router.esm.js?8c4f:1335)
    at Array.forEach (<anonymous>)
    at createRouterMatcher (vue-router.esm.js?8c4f:1335)
    at createRouter (vue-router.esm.js?8c4f:2064)
    at eval (index.js?a18c:26)
    at Module../src/router/index.js (app.js:1402)
    at __webpack_require__ (app.js:854)

I'm assuming this is because I don't prepend the path containing the asterisk with a '/', but if I do this then the catch all doesn't work. Here are my routes:

imports...

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/user',
    name: 'User',
    // route level code-splitting
    // this generates a separate chunk (user.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "user" */ '../views/user/User.vue'),
    children: [{path: '', component: UserStart}, {path: ':id', component: UserDetail}, {path: ':id/edit', component: UserEdit, name: 'userEdit'}]
  },
  {path: '/redirect-me', redirect: '/user'},
  {path: '*', redirect: '/'}
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

The wildcard route is the last object in the routes array. Does anyone know what I'm doing wrong?

user14131782
  • 736
  • 1
  • 11
  • 20

2 Answers2

74

Catch all routes (/*) must now be defined using a parameter with a custom regex: /:catchAll(.*)

For example:

    {
      // path: "*",
      path: "/:catchAll(.*)",
      name: "NotFound",
      component: PageNotFound,
      meta: {
        requiresAuth: false
      }
    }
Raj
  • 1,555
  • 18
  • 32
  • I'm trying to use dynamic route match, but when I set `path: "/:user"`, go to route `/user1234` and try to get it in the component with `$route.params.user`, it's `undefined`. When I `console.log($route)`, I got `params: { catchAll: "user1234" }`. How should I make the dynamic route match? – Andre Ravazzi Sep 09 '20 at 23:58
19

Personally, for Vue 2's * (star or catch all) routes in Vue 3 I use:

{
  path: '/:pathMatch(.*)*', <== THIS
  name: 'not-found',
  component: NotFound
}

Catch all routes (*, /*) must now be defined using a parameter with a custom regex:

The parameter name can be whatever you want like catchAll, pathMatch, noPage etc

{
  path: '/:pathMatch(.*)*', //will match everything and put it under `$route.params.pathMatch`
  name: 'not-found',
  component: NotFound
}
{ 
  path: '/user-:afterUser(.*)',// will match anything starting with `/user-` and put it under `$route.params.afterUser`
  component: UserGeneric
}


/:pathMatch(.*)*

  • The last * it is necessary if you plan on directly navigating to the not-found route using its name.

  • If you omit it the / character in params, it will be encoded when resolving or pushing.

For example if you use path: /:pathMatch(.*) (note: without the last asterisk) and you go to /user/not-found (a page that doesn't exists) the this.$route.params.pathMatch will be a string => 'user/not-found'

// bad example if using named routes:
router.resolve({
  name: 'bad-not-found',
  params: { pathMatch: 'not/found' },
}).href // '/not%2Ffound'

Instead, if you use path: /:pathMatch(.*)* (note: with asterisk) this.$route.params.pathMatch will be an array ['user', 'not-found']

// good example:
router.resolve({
  name: 'not-found',
  params: { pathMatch: ['not', 'found'] },
}).href // '/not/found'

Please read docs: From migration from vue 2 to vue 3 and Catch all / 404 Not found Route

Roland
  • 24,554
  • 4
  • 99
  • 97