Vue Router
Learn how to add routing to your Vue.js applications using Vue Router. Routing allows you to create single-page applications with multiple pages and navigation.
What is Vue Router?
Vue Router is the official routing library for Vue.js. It enables you to build single-page applications (SPAs) with multiple views, navigation, and browser history management.
Setting Up Vue Router
Installation
First, install Vue Router in your project:
npm install vue-router@4Basic Configuration
Create a router configuration file:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Contact from '../views/Contact.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/contact',
name: 'Contact',
component: Contact
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default routerIntegration with Vue App
Update your main application file to use the router:
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')Creating Route Components
Create your page components in a views directory:
<!-- views/Home.vue -->
<template>
<div class="home">
<h1>Welcome to Our Website</h1>
<p>This is the home page of our Vue.js application.</p>
</div>
</template>
<script>
export default {
name: 'Home'
}
</script>
<style scoped>
.home {
padding: 20px;
text-align: center;
}
</style><!-- views/About.vue -->
<template>
<div class="about">
<h1>About Us</h1>
<p>Learn more about our company and what we do.</p>
</div>
</template>
<script>
export default {
name: 'About'
}
</script>Navigation
Router Links
Use <router-link> for navigation between routes:
<!-- components/Navigation.vue -->
<template>
<nav class="navigation">
<router-link to="/" class="nav-link">Home</router-link>
<router-link to="/about" class="nav-link">About</router-link>
<router-link to="/contact" class="nav-link">Contact</router-link>
</nav>
</template>
<script>
export default {
name: 'Navigation'
}
</script>
<style scoped>
.navigation {
display: flex;
gap: 20px;
padding: 20px;
background-color: #f8f9fa;
}
.nav-link {
text-decoration: none;
color: #333;
padding: 8px 16px;
border-radius: 4px;
transition: background-color 0.3s;
}
.nav-link:hover,
.nav-link.router-link-active {
background-color: #42b883;
color: white;
}
</style>Programmatic Navigation
Navigate using JavaScript methods:
<template>
<div class="dashboard">
<h1>Dashboard</h1>
<button @click="goToProfile">View Profile</button>
<button @click="goBack">Go Back</button>
<button @click="goForward">Go Forward</button>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const goToProfile = () => {
router.push('/profile')
}
const goBack = () => {
router.back()
}
const goForward = () => {
router.forward()
}
</script>Dynamic Routes
Create routes that accept parameters:
// router/index.js
const routes = [
// ... other routes
{
path: '/user/:id',
name: 'UserProfile',
component: () => import('../views/UserProfile.vue')
},
{
path: '/blog/:slug',
name: 'BlogPost',
component: () => import('../views/BlogPost.vue')
}
]Accessing Route Parameters
<!-- views/UserProfile.vue -->
<template>
<div class="user-profile">
<h1>User Profile</h1>
<p>User ID: {{ userId }}</p>
<div v-if="user">
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</div>
<div v-else>
<p>Loading user data...</p>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const userId = route.params.id
const user = ref(null)
onMounted(async () => {
// Fetch user data based on userId
user.value = await fetchUser(userId)
})
async function fetchUser(id) {
// Simulate API call
return {
id: id,
name: 'John Doe',
email: '[email protected]'
}
}
</script>Nested Routes
Create child routes within parent components:
// router/index.js
const routes = [
{
path: '/dashboard',
component: () => import('../views/Dashboard.vue'),
children: [
{
path: '',
name: 'DashboardHome',
component: () => import('../views/DashboardHome.vue')
},
{
path: 'profile',
name: 'DashboardProfile',
component: () => import('../views/DashboardProfile.vue')
},
{
path: 'settings',
name: 'DashboardSettings',
component: () => import('../views/DashboardSettings.vue')
}
]
}
]Parent Component with Router View
<!-- views/Dashboard.vue -->
<template>
<div class="dashboard">
<header class="dashboard-header">
<h1>Dashboard</h1>
<nav class="dashboard-nav">
<router-link to="/dashboard">Home</router-link>
<router-link to="/dashboard/profile">Profile</router-link>
<router-link to="/dashboard/settings">Settings</router-link>
</nav>
</header>
<main class="dashboard-content">
<router-view />
</main>
</div>
</template>
<script>
export default {
name: 'Dashboard'
}
</script>
<style scoped>
.dashboard {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.dashboard-header {
background-color: #f8f9fa;
padding: 20px;
border-bottom: 1px solid #ddd;
}
.dashboard-nav {
margin-top: 15px;
display: flex;
gap: 15px;
}
.dashboard-nav a {
text-decoration: none;
color: #333;
padding: 8px 16px;
border-radius: 4px;
}
.dashboard-nav a.router-link-active {
background-color: #42b883;
color: white;
}
.dashboard-content {
flex: 1;
padding: 20px;
}
</style>Route Guards
Protect routes with navigation guards:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('../views/Dashboard.vue'),
meta: { requiresAuth: true }
},
{
path: '/admin',
name: 'Admin',
component: () => import('../views/Admin.vue'),
meta: { requiresAuth: true, requiresAdmin: true }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// Global navigation guard
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
if (to.meta.requiresAuth && !userStore.isAuthenticated) {
next('/login')
} else if (to.meta.requiresAdmin && userStore.userRole !== 'admin') {
next('/unauthorized')
} else {
next()
}
})
export default routerComponent-Level Guards
<!-- views/Profile.vue -->
<template>
<div class="profile">
<h1>Profile Page</h1>
<p>This page requires authentication.</p>
</div>
</template>
<script>
export default {
name: 'Profile',
beforeRouteEnter(to, from, next) {
// Called before the route that renders this component is confirmed
next(vm => {
// Access to component instance via `vm`
})
},
beforeRouteUpdate(to, from, next) {
// Called when the route that renders this component has changed
next()
},
beforeRouteLeave(to, from, next) {
// Called when the route that renders this component is about to be navigated away from
if (confirm('Are you sure you want to leave?')) {
next()
} else {
next(false)
}
}
}
</script>404 Error Handling
Create a catch-all route for 404 errors:
// router/index.js
const routes = [
// ... your other routes
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('../views/NotFound.vue')
}
]<!-- views/NotFound.vue -->
<template>
<div class="not-found">
<h1>404 - Page Not Found</h1>
<p>Sorry, the page you're looking for doesn't exist.</p>
<router-link to="/" class="home-link">Go Home</router-link>
</div>
</template>
<script>
export default {
name: 'NotFound'
}
</script>
<style scoped>
.not-found {
text-align: center;
padding: 50px 20px;
}
.home-link {
display: inline-block;
margin-top: 20px;
padding: 10px 20px;
background-color: #42b883;
color: white;
text-decoration: none;
border-radius: 4px;
}
</style>Best Practices
- Lazy load routes - Use dynamic imports for better performance
- Use meaningful route names - Makes programmatic navigation easier
- Implement proper error handling - Handle 404s and other errors gracefully
- Use route guards for authentication - Protect sensitive routes
- Keep routes organized - Group related routes together
Next Steps
Learn how to integrate with external APIs in the API integration tutorial.