Vue Router & Navigation
Vue Router is the official routing library for Vue.js. It deeply integrates with Vue core to make building Single Page Applications with Vue a breeze.
Router Setup
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/about',
name: 'About',
// Lazy-loaded route
component: () => import('../views/About.vue'),
},
{
path: '/users/:id',
name: 'UserProfile',
component: () => import('../views/UserProfile.vue'),
props: true, // Pass route params as props
},
{
path: '/dashboard',
component: () => import('../views/Dashboard.vue'),
children: [
{
path: '',
name: 'DashboardHome',
component: () => import('../views/DashboardHome.vue'),
},
{
path: 'settings',
name: 'Settings',
component: () => import('../views/Settings.vue'),
},
],
},
{
// 404 catch-all
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('../views/NotFound.vue'),
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
},
})
export default routerNavigation in Components
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
// Access route params
const userId = route.params.id
const query = route.query.search
// Programmatic navigation
const goToUser = (id) => {
router.push(`/users/${id}`)
}
const goBack = () => {
router.back()
}
const navigateWithQuery = () => {
router.push({
name: 'UserProfile',
params: { id: 123 },
query: { tab: 'posts' },
})
}
// Replace instead of push (no history entry)
const replaceRoute = () => {
router.replace('/new-location')
}
</script>
<template>
<div>
<!-- Declarative navigation -->
<router-link to="/">Home</router-link>
<router-link :to="{ name: 'About' }">About</router-link>
<router-link :to="`/users/${userId}`">User Profile</router-link>
<!-- Active link styling -->
<router-link
to="/dashboard"
active-class="active"
exact-active-class="exact-active"
>
Dashboard
</router-link>
<!-- Router view -->
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>Navigation Guards
Navigation guards allow you to protect routes, redirect users, or perform actions before/after navigation.
// Global before guard
router.beforeEach((to, from, next) => {
// Check authentication
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login')
} else {
next()
}
})
// Global after guard
router.afterEach((to, from) => {
// Analytics tracking
trackPageView(to.fullPath)
})
// Per-route guard
const routes = [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from) => {
if (!isAdmin()) {
return '/'
}
},
},
]
// In-component guards
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// Prevent leaving with unsaved changes
const hasUnsavedChanges = ref(false)
onBeforeRouteLeave((to, from) => {
if (hasUnsavedChanges.value) {
const answer = window.confirm('You have unsaved changes. Leave anyway?')
if (!answer) return false
}
})
// React to param changes in same route
onBeforeRouteUpdate((to, from) => {
// Fetch new user data when userId param changes
if (to.params.id !== from.params.id) {
fetchUser(to.params.id)
}
})
</script>Dynamic Routes & Nested Routes
const routes = [
// Dynamic segments
{
path: '/users/:id',
component: User,
},
{
path: '/posts/:id(\\d+)', // Regex: only numbers
component: Post,
},
{
// Optional params
path: '/search/:query?',
component: Search,
},
{
// Multiple params
path: '/articles/:category/:slug',
component: Article,
},
{
// Catch-all route
path: '/:pathMatch(.*)*',
component: NotFound,
},
{
// Nested routes
path: '/dashboard',
component: Dashboard,
children: [
{
path: '', // Default child route
component: DashboardHome,
},
{
path: 'profile',
component: Profile,
},
{
path: 'settings',
component: Settings,
children: [
{
path: 'account',
component: AccountSettings,
},
{
path: 'privacy',
component: PrivacySettings,
},
],
},
],
},
]Route Meta Fields & Lazy Loading
const routes = [
{
path: '/admin',
component: Admin,
meta: {
requiresAuth: true,
role: 'admin',
title: 'Admin Panel',
},
},
{
path: '/public',
component: Public,
meta: {
requiresAuth: false,
layout: 'public',
},
},
]
// Access meta in components
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
// Update page title based on meta
watch(
() => route.meta.title,
(title) => {
document.title = title || 'My App'
},
{ immediate: true }
)
</script>
// Lazy loading with named chunks
const routes = [
{
path: '/heavy',
component: () => import(/* webpackChunkName: "heavy" */ '../views/Heavy.vue'),
},
]Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free