Tutorial

Using Nested Routes in Vue.js

Vue.js

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

As your Vue.js Single Page Applications (SPAs) become moderately complex, you start to need Vue Router, and, moreover, nested routes. Nested routes allow for more complex user interfaces with components nested inside each other. Let’s build out a relatively simple use case that shows the utility of nested routes in Vue Router.

Setup with the Vue CLI

If you don’t have it installed already, install the Vue CLI globally by running either:

$ npm install -g @vue/cli

Or

$ yarn global add @vue/cli

Now you will be able to run the vue command from the command line. Let’s create a Vue application called alligator-nest:

$ vue create alligator-nest

Choose the default preset at the prompt (hit the enter key). After that, run the following command:

$ npm install vue-router

Then, go ahead and open up the alligator-nest directory in your editor of choice.

Base Code

The following CSS will help us with positioning elements for our UI. Add it as a stylesheet file in the public/ folder and reference it in public/index.html. We’ll be making use of CSS grid for this:

grid.css
.row1 {
  grid-row-start: 1;
  grid-row-end: 2;
}

.row12 {
  grid-row-start: 1;
  grid-row-end: 3;
}

.row123 {
  grid-row-start: 1;
  grid-row-end: 4;
}

.row2 {
  grid-row-start: 2;
  grid-row-end: 3;
}

.row23 {
  grid-row-start: 2;
  grid-row-end: 4;
}

.row3 {
  grid-row-start: 3;
  grid-row-end: 4;
}

.col1 {
  grid-column-start: 1;
  grid-column-end: 2;
}

.col12 {
  grid-column-start: 1;
  grid-column-end: 3;
}

.col123 {
  grid-column-start: 1;
  grid-column-end: 4;
}

.col1234 {
  grid-column-start: 1;
  grid-column-end: 5;
}

.col2 {
  grid-column-start: 2;
  grid-column-end: 3;
}

.col23 {
  grid-column-start: 2;
  grid-column-end: 4;
}

.col234 {
  grid-column-start: 2;
  grid-column-end: 5;
}

.col3 {
  grid-column-start: 3;
  grid-column-end: 4;
}

.col34 {
  grid-column-start: 3;
  grid-column-end: 5;
}

.col4 {
  grid-column-start: 4;
  grid-column-end: 5;
}

Next, let’s make some changes to the default files that vue-cli added.

Delete HelloWorld.vue from the src/components folder and delete any mention of it from src/App.vue. Make the following modifications to the HTML markup and CSS styling in App.vue.

<template>
  <div id="app">
    <h1 class="row1 col12">Alligator Nest</h1>
    <a class="row1 col3">Travels</a>
    <a class="row1 col4">About</a>
    <div class="row2 col234"></div>
  </div>
</template>
html, body {
  height: 100vh;
  width: 100vw;
  padding: 0;
  margin: 0;
}

#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  padding: 2%;
  height: 100%;
  display: grid;
  grid-template-rows: 20% 80%;
  grid-template-columns: 25% 25% 25% 25%;
}

If you run npm run serve in the project’s root directory you can mouse over to localhost:8080 in your browser and see a skeleton layout. Those display: grid properties are useful! Now we can begin routing.

Enter Vue Routing

Let’s whip up a component called AboutPage.vue in our /components folder. It will look like this:

<template>
  <div>
    <h2>About</h2>
    <p>Alligators were around during the time of the dinosaurs.</p>
  </div>
</template>

<script>
  export default {
    name: 'AboutPage',
  }
</script>

<style scoped>

</style>

Now our main.js file needs an /about route. It will look like this.

import VueRouter from 'vue-router';
import Vue from 'vue';
import App from './App.vue';

Vue.config.productionTip = false;

import VueRouter from 'vue-router';
Vue.use(VueRouter);

import AboutPage from './components/AboutPage.vue';

const routes = [
  { path: '/about', component: AboutPage },
]

const router = new VueRouter({
  routes
})

new Vue({
  render: h => h(App),
  router
}).$mount('#app');

Finally let’s go back to App.vue and change the anchor tag for “About” to a <router-link> tag with an attribute of to="/about". Then, change the second div to a <router-view> tag. Make sure to keep the grid positioning class attributes intact.

We now have a functional site skeleton with routing handled for the About page.

We’re focusing on the routing functionality here so we’re not going to get fancy with styling. Even so, the Travels page is going to look more elaborate.


To start, create a TravelPage the same way you created an AboutPage. Reference it in main.js.

Also create the following two components which will ultimately be nested in TravelPage.vue:

TravelAmericaPage.vue
<template>
  <div>
    <p>Alligators can be found in the American states of Louisiana and Florida.</p>
  </div>
</template>

<script>
  export default {
    name: 'TravelAmericaPage'
  }
</script>

<style scoped>
</style>
TravelChinaPage.vue
<template>
  <div>
    <p>Alligators can be found in China's Yangtze River Valley.</p>
  </div>
</template>

<script>
  export default {
    name: 'TravelChinaPage'
  }
</script>

<style scoped>

</style>

Nested route configuration

Now, let’s update both main.js and TravelPage.vue to reference those nested routes using children. main.js must be updated to have the following definition for the routes constant:

const routes = [
  {
    path: '/travel', component: TravelPage,
    children: [
      { path: '/travel/america', component: TravelAmericaPage },
      { path: '/travel/china', component: TravelChinaPage}
    ]
  },
  {
    path: '/about', component: AboutPage
  }
];

Note that the nesting of children can continue infinitely.

And TravelPage.vue can be written in the following way:

TravelPage.vue
<template>
  <div id="travel">
    <h2 class="row1">Travels</h2>
    <div class="flex-container row2">
      <router-link to="/travel/america">America</router-link>
      <router-link to="/travel/china">China</router-link>
    </div>
    <router-view class="row3"></router-view>
  </div>
</template>

<script>
  export default {
    name: 'TravelPage'
  }
</script>

<style scoped>
div {
  text-align: center;
}

#travel {
  display: grid;
  grid-template-rows: 20% 40% 40%;
}

.flex-container {
  display: flex;
  justify-content: space-around;
}
</style>

Check out localhost:8080 and you will see that the Travels page has 2 subpages within it! Our URLs update accordingly when you click on either link.

Conclusion

Hopefully this tutorial was useful for you in seeing how to get started with nested routes!

Other things to keep in mind on the topic — we could have routes defined with dynamic segments such as path: '/location/:id'. Then, on the views for those routes we can reference that id as this.$route.params. This is useful when you have more data of a particular type (users, pictures, etc.) that you are wishing to display on your site/app.

🐊 Later!

Creative Commons License