Real apps have multiple pages. Vue Router handles client-side navigation without full page reloads. This lesson covers setting up routes, dynamic route parameters, protecting routes, and lazy-loading components for faster initial loads.
npm install vue-router
import { createRouter, createWebHistory } from 'vue-router'
// Import page components
import HomeView from '@/views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// Lazy-load: only downloaded when the route is visited
component: () => import('@/views/AboutView.vue')
},
{
path: '/users/:id', // :id is a dynamic segment
name: 'user-detail',
component: () => import('@/views/UserDetailView.vue')
},
{
// 404 catch-all — must be last
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('@/views/NotFoundView.vue')
}
]
const router = createRouter({
history: createWebHistory(), // uses real URLs (no #)
routes
})
export default router
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import router from './router'
import App from './App.vue'
createApp(App).use(createPinia()).use(router).mount('#app')
Dynamic route segments like :id let one route match many URLs. Access the current route's params, query, and path with useRoute().
Loading user {{ route.params.id }}...
{{ user.name }}
{{ user.email }}
Navigation guards run code before a route is entered — perfect for authentication checks.
import { useAuthStore } from '@/stores/auth'
// Per-route guard using route metadata
const routes = [
{
path: '/dashboard',
component: () => import('@/views/DashboardView.vue'),
meta: { requiresAuth: true } // mark protected routes
}
]
// Global guard: runs before every navigation
router.beforeEach((to, from) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isLoggedIn) {
// Redirect to login, saving where they wanted to go
return { name: 'login', query: { redirect: to.fullPath } }
}
})
route.query.redirect || '/' so they land on the page they originally tried to visit. This is the expected UX pattern./users/:id), and 404.https://jsonplaceholder.typicode.com/users and display them on the Users page.RouterLink and active link styling.() => import('./View.vue') for better initial load performance.:id let one route serve many URLs. Read them with useRoute().params.router.push() for programmatic navigation, RouterLink in templates.