Vue Router — Routing for Vue.js Single-Page Applications
In this tutorial, you will learn about Vue Router. We cover key concepts, practical examples, and best practices to help you master this topic.
Vue Router is the official routing library for Vue.js SPAs with nested routes, named views, navigation guards, Lazy Loading, and seamless Vue integration.
What You'll Learn
By the end of this tutorial, you will understand how to set up Vue Router, define routes with nested layouts, use navigation guards for authentication, lazy load components, and handle route transitions.
Why It Matters
Vue Router is the standard routing solution for Vue.js applications. Understanding it is essential for building SPAs with Vue that have multiple views, URL-based navigation, and complex routing requirements.
Real-World Use
An e-commerce SPA built with Vue uses Vue Router for product listing, product detail, cart, and checkout routes. Nested routes handle product categories and subcategories. Navigation guards protect the checkout route until the user is authenticated.
Basic Setup
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: About
},
{
path: '/contact',
name: 'contact',
component: () => import('../views/Contact.vue') // lazy load
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('../views/NotFound.vue')
}
];
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) return savedPosition;
return { top: 0 };
}
});
export default router;
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
Nested Routes
const routes = [
{
path: '/dashboard',
component: () => import('../layouts/DashboardLayout.vue'),
children: [
{
path: '',
name: 'dashboard',
component: () => import('../views/DashboardOverview.vue')
},
{
path: 'users',
name: 'users',
component: () => import('../views/UsersList.vue')
},
{
path: 'users/:id',
name: 'user-detail',
component: () => import('../views/UserDetail.vue'),
props: true
},
{
path: 'settings',
name: 'settings',
component: () => import('../views/Settings.vue'),
meta: { requiresAuth: true }
}
]
}
];
Navigation Guards
// router/index.js
import { useAuthStore } from '../stores/auth';
const router = createRouter({ ... });
// Global guard
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore();
// Check if route requires authentication
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
// Redirect to login with return URL
next({
name: 'login',
query: { redirect: to.fullPath }
});
return;
}
// Check if user has required role
if (to.meta.role && !authStore.hasRole(to.meta.role)) {
next({ name: 'forbidden' });
return;
}
next();
});
// After navigation
router.afterEach((to, from) => {
// Update page title
document.title = to.meta.title
? `${to.meta.title} — My App`
: 'My App';
// Send analytics
if (typeof gtag !== 'undefined') {
gtag('config', 'GA_MEASUREMENT_ID', {
page_path: to.fullPath
});
}
});
Lazy Loading
// Lazy load all route components
const routes = [
{
path: '/',
name: 'home',
component: () => import('../views/Home.vue')
},
{
path: '/products',
name: 'products',
component: () => import('../views/Products.vue'),
children: [
{
path: ':id',
name: 'product-detail',
component: () => import('../views/ProductDetail.vue')
}
]
}
];
// Show loading state while component loads
// In App.vue:
// <router-view v-slot="{ Component }">
// <transition name="fade" mode="out-in">
// <Suspense>
// <component :is="Component" />
// <template #fallback>
// <div class="loading">Loading...</div>
// </template>
// </Suspense>
// </transition>
// </router-view>
Route Transitions
// App.vue
<template>
<router-view v-slot="{ Component, route }">
<transition :name="route.meta.transition || 'fade'" mode="out-in">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
</template>
<style>
.fade-enter-active, .fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.slide-enter-active, .slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
}
.slide-leave-to {
transform: translateX(-100%);
}
</style>
Common Mistakes
- Not using createWebHistory for clean URLs. Hash mode (#/path) is default. Use createWebHistory() for clean URLs and configure server fallback.
- Forgetting the catch-all route. Without pathMatch, unmatched routes show nothing. Always add a 404 route at the end. 3.** Not handling navigation failures.** Navigation can fail (guard rejection, component load error). Use router.onError() for global error handling.
- Overusing route guards for simple checks. Use component-level logic for UI conditions (showing/hiding elements) and route guards only for actual access control.
- Not using route names. Named routes are easier to maintain than hardcoded paths. Use router.push({ name: 'user', params: { id: 1 } }) instead of /users/1.
Practice Questions
- What is the difference between createWebHistory and createWebHashHistory?
- How do nested routes work with Vue Router?
- How do you implement authentication guards in Vue Router?
- How do you lazy load route components?
- How do you add page transitions between routes?
Challenge: Build a Vue 3 SPA with Vue Router: public routes (home, about, contact), authenticated dashboard with nested routes (overview, users/:id, settings), lazy-loaded components, navigation guard that redirects to login, and route transitions.
FAQ
Mini Project
Build a multi-page Vue 3 SPA with Vue Router: a home page, a blog section with list and detail views, an admin panel with nested routes and auth guard, lazy loading for all route components, and smooth page transitions. Use named routes and programmatic navigation.
What's Next
You mastered routing. Now learn about state management — managing shared data across your SPA components.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro