URL: https://www.progressiverobot.com/vuejs-nested-routes/

Introduction

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.

In this article, you will build out an example Vue.js project that highlights the utility of nested routes.

Prerequisites

nested illustration for: Prerequisites

To complete this tutorial, you will need:

This tutorial was verified with Node v16.5.0, npm v7.20.0, vue v2.6.14, and vue-router v3.5.2.

Step 1 — Setting Up the Project

To quickly set up the project, this article will recommend using @vue/cli.

Note: This article will take the approach of using npx to avoid a global installation of @vue/cli;

				
					
npx @vue/cli create <^>vue-nested-routes-example<^>

				
			

When prompted with configurations options, you may wish to Manually select features and include Router.

Then, navigate to the newly created project directory:

				
					
cd <^>vue-nested-routes-example<^>

				
			

If at this point you did not install Vue Router at creation, you can add it to your project now.

Step 2 — Building the Application

The following CSS will help us with positioning elements for our user interface.

Under the 'public directory, create a grid.css file:

				
					
[label public/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;

}

				
			

We'll be making use of CSS grid.

Add grid.css to index.html:

				
					
[label public/index.html]

<!DOCTYPE html>

<html lang="">

 <head>

 <meta charset="utf-8">

 <meta http-equiv="X-UA-Compatible" content="IE=edge">

 <meta name="viewport" content="width=device-width,initial-scale=1.0">

 <link rel="icon" href="<%= BASE_URL %>favicon.ico">

 <^><link rel="stylesheet" href="grid.css"><^>

 <title><%= htmlWebpackPlugin.options.title %></title>

 </head>

 <body>

 <noscript>

 <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>

 </noscript>

 <div id="app"></div>

 <!-- built files will be auto injected -->

 </body>

</html>

				
			

Next, let's make some changes to the default files that @vue/cli created.

Open App.vue in your code editor. And make the following modifications to the HTML markup in App.vue:

				
					
[label src/App.vue]

<template>

 <div id="app">

 <h1 class="row1 col12">Example</h1>

 <a class="row1 col3">About</a>

 <a class="row1 col4">Nested Pages</a>

 <div class="row2 col234"></div>

 </div>

</template>

				
			

And CSS styling in App.vue:

				
					
[label src/App.vue]

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%;

}

				
			

Save the changes to your files.

Then run the following command in your terminal:

				
					
npm run serve

				
			

Visit localhost:8080 in your web browser and observe the grid layout.

Now we can begin routing.

Step 3 — Applying Vue Routing

@vue/cli will build a main.js file:

				
					
[label src/main.js]

import Vue from 'vue'

import App from './App.vue'

import router from './router'



Vue.config.productionTip = false



new Vue({

 router,

 render: h => h(App)

}).$mount('#app')

				
			

And a router/index.js file:

				
					
[label src/router/index.js]

import Vue from 'vue'

import VueRouter from 'vue-router'

import Home from '../views/Home.vue'



Vue.use(VueRouter)



const routes = [

 {

 path: '/',

 name: 'Home',

 component: Home

 },

 {

 path: '/about',

 name: 'About',

 // route level code-splitting

 // this generates a separate chunk (about.[hash].js) for this route

 // which is lazy-loaded when the route is visited.

 component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')

 }

]



const router = new VueRouter({

 mode: 'history',

 base: process.env.BASE_URL,

 routes

})



export default router

				
			

And Home.vue and About.vue files in the views directory.

Let's revisit 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.

				
					
[label src/App.vue]

&lt;template&gt;

 &lt;div id="app"&gt;

 &lt;h1 class="row1 col12"&gt;Example&lt;/h1&gt;

 &lt;^&gt;&lt;router-link to="/about" class="row1 col3"&gt;About&lt;/router-link&gt;&lt;^&gt;

 &lt;a class="row1 col4"&gt;Nested Pages&lt;/a&gt;

 &lt;^&gt;&lt;router-view class="row2 col234" /&gt;&lt;^&gt;

 &lt;/div&gt;

&lt;/template&gt;

				
			

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

Step 4 — Building a Nested View

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

To start, create a NestedPages.vue file in the views directory:

				
					
[label src/views/NestedPages.vue]

&lt;template&gt;

 &lt;div id="nested"&gt;

 &lt;h2 class="row1"&gt;Nested Pages&lt;/h2&gt;

 &lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

 export default {

 name: 'NestedPages'

 }

&lt;/script&gt;



&lt;style scoped&gt;

div {

 text-align: center;

}

&lt;/style&gt;

				
			

And reference it in router/index.js:

				
					
[label src/router/index.js]

import Vue from 'vue'

import VueRouter from 'vue-router'

import Home from '../views/Home.vue'

&lt;^&gt;import NestedPages from '../views/NestedPages.vue'&lt;^&gt;



Vue.use(VueRouter)



const routes = [

 {

 path: '/',

 name: 'Home',

 component: Home

 },

 {

 path: '/about',

 name: 'About',

 // route level code-splitting

 // this generates a separate chunk (about.[hash].js) for this route

 // which is lazy-loaded when the route is visited.

 component: () =&gt; import(/* webpackChunkName: "about" */ '../views/About.vue')

 },

 &lt;^&gt;{&lt;^&gt;

 &lt;^&gt;path: '/nested',&lt;^&gt;

 &lt;^&gt;component: NestedPages&lt;^&gt;

 &lt;^&gt;}&lt;^&gt;

]



const router = new VueRouter({

 mode: 'history',

 base: process.env.BASE_URL,

 routes

})



export default router

				
			

Also, create the following two components which will ultimately be nested in NestedPages.vue.

Create a NestedPageOne.vue file:

				
					
[label src/components/NestedPageOne.vue]

&lt;template&gt;

 &lt;div&gt;

 &lt;p&gt;Nested Page One&lt;/p&gt;

 &lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

 export default {

 name: 'NestedPageOne'

 }

&lt;/script&gt;



&lt;style scoped&gt;

&lt;/style&gt;

				
			

And create a similiar NestedPageTwo.vue file:

				
					
[label src/components/NestedPageTwo.vue]

&lt;template&gt;

 &lt;div&gt;

 &lt;p&gt;Nested Page Two&lt;/p&gt;

 &lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

 export default {

 name: 'NestedPageTwo'

 }

&lt;/script&gt;



&lt;style scoped&gt;

&lt;/style&gt;

				
			

Finally, revisit App.vue and update the Nested Pages link to use <router-link>:

				
					
[label src/App.vue]

&lt;template&gt;

 &lt;div id="app"&gt;

 &lt;h1 class="row1 col12"&gt;Example&lt;/h1&gt;

 &lt;router-link to="/about" class="row1 col3"&gt;About&lt;/router-link&gt;

 &lt;^&gt;&lt;router-link to="/nested" class="row1 col4"&gt;Nested Pages&lt;/router-link&gt;&lt;^&gt;

 &lt;router-view class="row2 col234" /&gt;

 &lt;/div&gt;

&lt;/template&gt;

				
			

At this point, you have a new nested route and two new components.

Step 5 — Configuring Nested Children Routes

Revisit NestedPages.vue and add <router-link> for Nested Page One and Nested Page Two.

				
					
[label src/views/NestedPages.vue]

&lt;template&gt;

 &lt;div id="nested"&gt;

 &lt;h2 class="row1"&gt;Nested Pages&lt;/h2&gt;

 &lt;^&gt;&lt;div class="flex-container row2"&gt;&lt;^&gt;

 &lt;^&gt;&lt;router-link to="/nested/one"&gt;Nested Page One&lt;/router-link&gt;&lt;^&gt;

 &lt;^&gt;&lt;router-link to="/nested/two"&gt;Nested Page Two&lt;/router-link&gt;&lt;^&gt;

 &lt;^&gt;&lt;/div&gt;&lt;^&gt;

 &lt;^&gt;&lt;router-view class="row3" /&gt;&lt;^&gt;

 &lt;/div&gt;

&lt;/template&gt;



&lt;script&gt;

 export default {

 name: 'NestedPages'

 }

&lt;/script&gt;



&lt;style scoped&gt;

div {

 text-align: center;

}



&lt;^&gt;#nested {&lt;^&gt;

 &lt;^&gt;display: grid;&lt;^&gt;

 &lt;^&gt;grid-template-rows: 20% 40% 40%;&lt;^&gt;

&lt;^&gt;}&lt;^&gt;



&lt;^&gt;.flex-container {&lt;^&gt;

 &lt;^&gt;display: flex;&lt;^&gt;

 &lt;^&gt;justify-content: space-around;&lt;^&gt;

&lt;^&gt;}&lt;^&gt;

&lt;/style&gt;

				
			

Now, let's update router/index.js to reference those nested routes using children.

				
					
[label router/index.js]

// ...



&lt;^&gt;import NestedPageOne from '../components/NestedPageOne.vue'&lt;^&gt;

&lt;^&gt;import NestedPageTwo from '../components/NestedPageTwo.vue'&lt;^&gt;



// ...



const routes = [

 // ...

 {

 path: '/nested',

 component: NestedPages,

 &lt;^&gt;children: [&lt;^&gt;

 &lt;^&gt;{

 &lt;^&gt;path: '/nested/one',&lt;^&gt;

 &lt;^&gt;component: NestedPageOne&lt;^&gt;

 &lt;^&gt;},&lt;^&gt;

 &lt;^&gt;{&lt;^&gt;

 &lt;^&gt;path: '/nested/two',&lt;^&gt;

 &lt;^&gt;component: NestedPageTwo&lt;^&gt;

 &lt;^&gt;}&lt;^&gt;

 &lt;^&gt;]&lt;^&gt;

 },

 // ...

];



// ...

				
			

Note: Note that the nesting of children can continue infinitely.

Save the changes to your files.

Then run the following command in your terminal:

				
					
npm run serve

				
			

Revisit localhost:8080 in your web browser. When you visit the /nested route, you can visit the NestedPageOne and NestedPageTwo children routes.

Conclusion

In this article, you built out an example Vue.js project that highlights the utility of 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 or app.