Vue Router 4–404 and Nested Routes

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Vue Router 4 is in beta and it’s subject to change.

To build a single page app easily, we got to add routing so that URLs will be mapped to components that are rendered.

In this article, we’ll look at how to use Vue Router 4 with Vue 3.

Catch-All / 404 Not Found Route

We can create a catch-all or 404 route by using the asterisk pattern as the path.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/foo">Foo</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Foo = {
        template: "<div>foo</div>"
      };

      const NotFound = {
        template: "<div>not found</div>"
      };

      const routes = [
        { path: "/foo", component: Foo },
        { path: "/:catchAll(.*)", component: NotFound }
      ];

      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({});
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We define a catch-all route with the /:catchAll(.*) capture group instead of * as in Vue Router 3.

Now when we go to any path that isn’t foo , we’ll see the ‘not found’ message.

Nested Routes

We can create nested routes with the children property.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/parent/home">parent</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Parent = {
        template: `<div>
          parent
          <router-view></router-view>
        </div>`
      };

      const Home = {
        template: `<div>home</div>`
      };

       const routes = [
        {
          path: "/parent",
          component: Parent,
          children: [{ path: "home", component: Home }]
        }
      ];

      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({
        watch: {
          $route() {
            console.log(this.$route.resolve);
          }
        }
      });
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We have a routes array with an object that has the children property.

The property has an object with the path and component properties like a regular route.

In the router-link , the to prop has the path to the nested route.

The Parent component has the router-view component so that we can view the content of the child route.

Therefore, when we click on the parent link, we see the:

parent
home
Enter fullscreen mode Exit fullscreen mode

text displayed.

We can have URL parameters in the parent route.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://unpkg.com/vue@next"></script>
    <script src="https://unpkg.com/vue-router@4.0.0-beta.7/dist/vue-router.global.js"></script>
    <title>App</title>
  </head>
  <body>
    <div id="app">
      <p>
        <router-link to="/parent/1/home">parent</router-link>
      </p>
      <router-view></router-view>
    </div>
    <script>
      const Parent = {
        template: `<div>
          <div>parent {{ $router.currentRoute.value.params.id }}</div>
          <div><router-view></router-view></div>
        </div>`
      };

      const Home = {
        template: `<div>home</div>`
      };

      const routes = [
        {
          path: "/parent/:id",
          component: Parent,
          children: [{ path: "home", component: Home }]
        }
      ];

      const router = VueRouter.createRouter({
        history: VueRouter.createWebHistory(),
        routes
      });

      const app = Vue.createApp({
        watch: {
          $route() {
            console.log(this.$route.resolve);
          }
        }
      });
      app.use(router);
      app.mount("#app");
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

To add the :id URL parameter placeholder.

Since the router-link ‘s to prop is now /parent/1/home and we have the $router.currentRoute.value.params.id in the Parent ‘s template, we’ll see the number 1 displayed.

The URL parameter placeholder of the parent stays with the parent.

Conclusion

The way we define 404 and nested routes are slightly different from Vue Router 3 in Vue Router 4.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .