Defining Nested Routes with Vue Router

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.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to define nested routes with Vue Router.

Why do we Need Nested Routes?

We need nested routes to organize routes where components are nested multiple levels deep.

For example, we have to find a way to organize routes like:

/user/bar/profile                       
/user/bar/posts
Enter fullscreen mode Exit fullscreen mode

into one coherent place.

Defining Nested Routes

We can define nested routes by adding a children array in the route entry, and then in the parent route’s template we have to add router-view to display the children routes.

For example, we can write the following:

src/index.js :

const Profile = { template: "<p>{{$route.params.user}}'s profile</p>" };  
const Posts = { template: "<p>{{$route.params.user}}'s posts</p>" };  
const User = {  
  template: `  
    <div>  
      <p>user - {{$route.params.user}}</p>  
      <router-view></router-view>  
    </div>  
`  
};  
const routes = [  
  {  
    path: "/user/:user",  
    component: User,  
    children: [  
      { path: "posts", component: Posts },  
      { path: "profile", component: Profile }  
    ]  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});
Enter fullscreen mode Exit fullscreen mode

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="[https://unpkg.com/vue/dist/vue.js](https://unpkg.com/vue/dist/vue.js)"></script>  
    <script src="[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)"></script>  
  </head>  
  <body>  
    <div id="app">  
      <div>  
        <router-link to="/user/foo">Foo User</router-link>  
        <router-link to="/user/foo/posts">Foo Post</router-link>  
        <router-link to="/user/foo/profile">Foo Profile</router-link>  
      </div>  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
Enter fullscreen mode Exit fullscreen mode

In the code above, we have:

const routes = [  
  {  
    path: "/user/:user",  
    component: User,  
    children: [  
      { path: "posts", component: Posts },  
      { path: "profile", component: Profile }  
    ]  
  }  
];
Enter fullscreen mode Exit fullscreen mode

to define the child routes.

The Posts and Profile components are like any component we defined as usual.

However, the User component has the router-view to display them content of the Posts and Profile components.

In the template, we have 3 router links to go to the root and child routes:

<router-link to="/user/foo">Foo User</router-link>  
<router-link to="/user/foo/posts">Foo Post</router-link>  
<router-link to="/user/foo/profile">Foo Profile</router-link>
Enter fullscreen mode Exit fullscreen mode

Then we should see:

Foo User Foo Post Foo Profile  
user - foofoo's posts
Enter fullscreen mode Exit fullscreen mode

when we go to /#/user/foo/posts or click on Foo Post .

And we should see:

Foo User Foo Post Foo Profileuser - foofoo's profile
Enter fullscreen mode Exit fullscreen mode

when we go to /#/user/foo/profile or click on Foo Profile.

When we go to /#/user/foo or click on Foo User , we see:

Foo User Foo Post Foo Profileuser - foo
Enter fullscreen mode Exit fullscreen mode

Any nested path that starts with / will be treated as the root path. This lets us use component nesting without having to use a nested URL.

Also, the parent and child routes all have access to the same route parameters.

For example, we can add a UserHome component and a route for it to the example above as follows:

src/index.js :

const Profile = { template: "<p>{{$route.params.user}}'s profile</p>" };  
const Posts = { template: "<p>{{$route.params.user}}'s posts</p>" };  
const UserHome = { template: "<p>{{$route.params.user}} home</p>" };  
const User = {  
  template: `  
    <div>  
      <p>user - {{$route.params.user}}</p>  
      <router-view></router-view>  
    </div>`  
};  
const routes = [  
  {  
    path: "/user/:user",  
    component: User,  
    children: [  
      { path: "", component: UserHome },  
      { path: "posts", component: Posts },  
      { path: "profile", component: Profile }  
    ]  
  }  
];

const router = new VueRouter({  
  routes  
});

new Vue({  
  el: "#app",  
  router  
});
Enter fullscreen mode Exit fullscreen mode

index.html :

<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <div>  
        <router-link to="/user/foo">Foo User</router-link>  
        <router-link to="/user/foo/posts">Foo Post</router-link>  
        <router-link to="/user/foo/profile">Foo Profile</router-link>  
      </div>  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
Enter fullscreen mode Exit fullscreen mode

Now when we go to /user/foo or click on Foo User , we see:

Foo User Foo Post Foo Profileuser - foofoo home
Enter fullscreen mode Exit fullscreen mode

Everything else remains the same.

Conclusion

We can add nested routes to our routes by adding a children property with an array of routes.

Then we have to add router-view to the parent route so that we can see the content of the child routes.

The parent and child routes all have access to the same route parameters.

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