One of the most powerful features of modern single-page web applications (SPA) is routing. Modern single-page apps such as a Vue application can transition from page to page on the client-side (without requesting the server). Vue Router is the official library for page navigation in Vue applications. Vue Router is simple to use, yet powerful. In this article, we will deep dive into Vue Router 4 (used with Vue 3). We will go over everything you need to know to use Vue Router comfortably.
We will cover:
Whether you are a beginner just getting into Vue Router or a seasoned JavaScript/Vue developer looking to skill up, there’s something for you in this tutorial. Let’s get started.
👉 We have also created a completely free in-depth Vue Router video course. If you learn better by watching videos, we highly recommend you give it a shot. Click here to watch the Vue Router course.
Let’s create a new project and get right into coding. We’ll create a new project with vue-cli. If you don’t have it installed you can install it by running npm install -g @vue/cli
.
# Create a new Vue project
vue create myapp
cd myapp
# start your app by running the following command
npm run serve
Next we can add the Vue Router to our project by running the following command.
vue add router
The cli
will ask if we want history mode. For this tutorial we’ll be using history mode, so we’ll select yes.
Let’s take a look at the folder structure that was created for us.
A folder named views
is created for us along with two files About.vue
and Home.vue
. These files represent our pages. A file named router/index.js
file is also generated. This file contains all the router configurations. Let’s open up the router/index.js
file.
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
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 = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
We are importing createRouter
and createWebHistory
from the vue-router
library. Next, we import the Home component from views/Home.vue
. On line 3 we are declaring an array of objects named routes. This array represents our routes in the application. These array items are called route objects. The first route object has a path /
which means this is going to be our base URL. The component property represents what component will be rendered when the user visits this path. We will render the Home page on this path. Finally, the name property represents the name of the route.
We are pretty much following the same logic for the /about
the path. However, instead of directly importing the component we are importing it through Webpack’s code splitting feature. More on this later.
Now let’s jump into App.vue
file.
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<router-view/>
</template>
<style>
</style>
Observe the router-link
tags. These tags are just fancy anchor links. However unlike an anchor link (<a href="">
tag) the <router-link>
will not reload the whole page. Remember Vue is a single-page application. The data for the app is already downloaded from the server. When we route to another view the application just hides some information and displays the requested information. router-link
tags have a to
property which refers to which page to visit. The <router-view/>
tag is what renders the right component when navigation links are triggered.
Let’s add our own route to this application.
// App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<router-link to="/profile">Profile</router-link>
</div>
<router-view/>
</template>
We need to add a new router-link to App.vue
. Next we create a new component in the views directory.
// views/Profile.vue
<template>
<div class="home">
Profile Info
</div>
</template>
Finally, in our routes array we have to import this and specify the route path.
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Profile from '../views/Profile.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/profile',
name: 'profile',
component: Profile,
},
{
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 = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
That’s it we have our own routes added to the application.
First of all, let’s take a look at what router parameters (router params in short) are and why they are important. Let’s say we have a page in our application where we show users a list of all the blog posts available.
Let’s say in our application this path is /posts
. When a user clicks on any of these articles the user is taken to that article page. For each article the path will be different (/posts/article-1
, /posts/article-2
, etc.). How do we achieve this? We can add a new route for every post we have, but then we have to change our code every time a new post is published. Therefore, we will use a dynamic route. This is where router params comes in handy.
In our application, we can specify a generic route like the /posts
and, then we can specify optional route params to the same route like this /posts/:id
. The :id
picks up any dynamic string as a parameter. This means we can now route to paths like /posts/article-1
, /posts/article-2
, etc. and the Vue router in the application will know which path we are trying to go to.
Let’s see this in action inside our application.
// App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link :to="{ name: 'profile', params: { username: 'mark2021' } }" >
Profile
</router-link>
</div>
<router-view/>
</template>
In our router-link
tag we can use the :to
property to pass in an object with name and params data. The params object can be any type of data (i.e. strings, numbers, objects). In the code snippet above we are passing in an object with username
with a string value (mark2021).
When we add Vue router to our Vue application we get access to a special $route
object inside the global scope of our application. This object holds all the router information of our application. We can access $route
object by simply calling this.$route
. The params information is also available within this $route
object. We can access the params
object inside of the Profile component through the $route
object. Take a look at the code snippet below.
<template>
<div class="profile">
Profile Info
</div>
<span>
{{ this.$route.params.username }}
</span>
</template>
Below is a quick reference table to help you visualize how dynamic routes work.
route pattern | URL path | $this.route.params |
---|---|---|
/product/:name | /product/apple-watch | { name: "apple-watch"} |
/product/:name/comment/:comment_id | /product/apple-watch/comment/123 | { name: 'apple-watch', comment_id: '123' } |
In the previous section we saw how, we can pass in additional information with <router-link>
. Dynamic routes are a similar concept. In this scenario the parameter is a part of the URL
itself.
Let’s take a look how we can define dynamic routes in our application. Let’s create a new component under view and call it Product
.
<template>
<div>
Product Page
</div>
<span>
Looking for Product: {{ this.$route.params.id }}
</span>
</template>
Now we can add this component to our routes.
const routes = [
// rest of the code ...
{ path: '/product/:id', component: Product },
]
Notice we appended a dynamic parameter to :id
to the URL. Now if you visit http://localhost:8080/product/12343
in our browser you can see that the component will have access to the dynamic parameter that was passed through the URL.
We could then use this Id
to fetch the actual product from our API in a lifecycle hook.
We can also have multiple dynamic parameters in our URL. The dynamic segments will map to corresponding fields of $route.params
.
Let’s take a look at how it works. we will first make some quick changes in our product route.
const routes = [
// rest of the code ...
{ path: '/product/:id/:category', component: () => import('../views/Product.vue') }
]
Instead of one param :id
, now we are specifying two params. Now let’s go the path http://localhost:8080/product/12343/electronics
and open up the Vue devtools. As you can see from the screenshot below we have now access to params and these params are mapping to specified variable name id
and category
.
Let’s talk about lazy loading. This is one of the advanced features that Vue Router provides out of the box. Let’s open up our devtools and observe the js
tab.
You can see that when we load the application for the first time it loads an app.js
script. When you navigate to /profile
route or /product
route you can see no new javascript is being loaded. This is because all the JavaScript code for those components was already loaded with app.js
. In a smaller application, this is not a problem. However, what if we have a large application. In that case, loading all the JS code in one go can make the page load time significantly longer. Lazy loading is used to avoid this.
When we lazy load, we load only the parts we need, when we need them.
// router/index.js
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/profile',
name: 'profile',
component: Profile,
},
{ path: '/product/:id', component: Product },
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
Take a look at line 17 of router/index.js
. Do you recall this from an earlier example? The About
component here is being imported with an arrow function. This is an example of lazy loading. Unlike Home
, Profile
and Product
component the About
component is not loaded with the app.js
. The About
component will only get loaded when a user visits the /about
route. This is all we have to do to implement lazy load. This lazy loading is being done through Webpack’s code splitting feature. Under the hood Vue uses Webpack as a bundler. You can learn more about advanced Vue features such as code splitting, Webpack configuration etc. in depth in our Vue 3 Masterclass course.
We can update our code to use lazy loading for each route.
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/profile',
name: 'profile',
component: () => import('../views/Profile.vue')
},
{ path: '/product/:id', component: () => import('../views/Product.vue') },
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
💡 If you are looking to distinguish yourself as a developer with a deep understanding of Vue then give our Vue 3 Masterclass a shot. In this course, we delve deep into some of the advanced features of Vue such as higher-order functions, code splitting, Webpack configuration, creating vue plugins, route guards and much more.
In our applications, there will be scenarios when we would want to programmatically navigate to another page. Inside our application, we have access to the router instance as $router
. We can access it by calling **this.$router**
. The router instance has a function called push. This function can be used to navigate to different routes. Here are some examples of how it works
// route by route path
this.$router.push('home')
// object
this.$router.push({ path: 'home' })
// named route navigation with parameters
this.$router.push({ name: 'user', params: { userId: '123' } })
// with query params, resulting in /profile?info=some-info
this.$router.push({ path: 'profile', query: { info: 'some-info' } })
I hope this article gave you an overview of the different functionalities of Vue Router. We discussed the most important and commonly used features of Vue Router. Feel free to visit the vue router documentation for more in-depth details about the various APIs. Here at VueSchool we have created a completely free course on Vue Router. For more in-depth Vue learning material you can also take a look at our Vue 3 Masterclass course.
We also have a Vue Router 3 For Everyone course for Vue.js 2.
That’s all for today, happy hacking.
© All rights reserved. Made with ❤️ by BitterBrains, Inc.