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.
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.
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.
The following CSS will help us with positioning elements for our user interface.
Under the 'public
directory, create a grid.css
file:
.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
:
<!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
:
<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
:
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.
@vue/cli
will build a main.js
file:
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:
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.
<template>
<div id="app">
<h1 class="row1 col12">Example</h1>
<router-link to="/about" class="row1 col3">About</router-link>
<a class="row1 col4">Nested Pages</a>
<router-view class="row2 col234" />
</div>
</template>
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 Nested
page is going to look more elaborate.
To start, create a NestedPages.vue
file in the views
directory:
<template>
<div id="nested">
<h2 class="row1">Nested Pages</h2>
</div>
</template>
<script>
export default {
name: 'NestedPages'
}
</script>
<style scoped>
div {
text-align: center;
}
</style>
And reference it in router/index.js
:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import NestedPages from '../views/NestedPages.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')
},
{
path: '/nested',
component: NestedPages
}
]
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:
<template>
<div>
<p>Nested Page One</p>
</div>
</template>
<script>
export default {
name: 'NestedPageOne'
}
</script>
<style scoped>
</style>
And create a similiar NestedPageTwo.vue
file:
<template>
<div>
<p>Nested Page Two</p>
</div>
</template>
<script>
export default {
name: 'NestedPageTwo'
}
</script>
<style scoped>
</style>
Finally, revisit App.vue
and update the Nested Pages link to use <router-link>
:
<template>
<div id="app">
<h1 class="row1 col12">Example</h1>
<router-link to="/about" class="row1 col3">About</router-link>
<router-link to="/nested" class="row1 col4">Nested Pages</router-link>
<router-view class="row2 col234" />
</div>
</template>
At this point, you have a new nested
route and two new components.
Revisit NestedPages.vue
and add <router-link>
for Nested Page One and Nested Page Two.
<template>
<div id="nested">
<h2 class="row1">Nested Pages</h2>
<div class="flex-container row2">
<router-link to="/nested/one">Nested Page One</router-link>
<router-link to="/nested/two">Nested Page Two</router-link>
</div>
<router-view class="row3" />
</div>
</template>
<script>
export default {
name: 'NestedPages'
}
</script>
<style scoped>
div {
text-align: center;
}
#nested {
display: grid;
grid-template-rows: 20% 40% 40%;
}
.flex-container {
display: flex;
justify-content: space-around;
}
</style>
Now, let’s update router/index.js
to reference those nested routes using children
.
// ...
import NestedPageOne from '../components/NestedPageOne.vue'
import NestedPageTwo from '../components/NestedPageTwo.vue'
// ...
const routes = [
// ...
{
path: '/nested',
component: NestedPages,
children: [
<^>{
path: '/nested/one',
component: NestedPageOne
},
{
path: '/nested/two',
component: NestedPageTwo
}
]
},
// ...
];
// ...
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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.