Vue.js Router
Vue Router is the official routing library for Vue.js. It lets you build multi-page applications where different URLs show different components — without ever reloading the browser page. The user navigates between pages and the URL updates, but only the content area changes.
What Routing Does
In a traditional website, clicking a link loads an entirely new HTML page from the server. In a Vue single-page application (SPA), the browser loads one HTML file and Vue Router swaps components in and out based on the URL.
Diagram: Traditional Website vs Vue Router SPA
Traditional Website: User visits /home → Server sends home.html (full page load) User visits /about → Server sends about.html (full page load) User visits /contact→ Server sends contact.html (full page load) Each click = complete page refresh Vue SPA with Router: Browser loads index.html once User visits /home → Vue renders HomeView component (instant) User visits /about → Vue renders AboutView component (instant) User visits /contact→ Vue renders ContactView component (instant) No page refresh — only the content area changes
Installation
When creating a project with Vite, select "Yes" for Vue Router. For an existing project, install it with:
npm install vue-router@4
Setting Up Vue Router
You define routes in a configuration file, create the router, and plug it into your Vue app.
Step 1: Create the Router File
// src/router/index.js
import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";
import AboutView from "../views/AboutView.vue";
import ContactView from "../views/ContactView.vue";
const routes = [
{ path: "/", component: HomeView },
{ path: "/about", component: AboutView },
{ path: "/contact", component: ContactView }
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
Step 2: Register the Router in main.js
// src/main.js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
createApp(App).use(router).mount("#app");
Step 3: Add RouterView to App.vue
<!-- src/App.vue -->
<template>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<RouterLink to="/contact">Contact</RouterLink>
</nav>
<RouterView /> <!-- Active component renders here -->
</template>
Diagram: RouterView and RouterLink
App.vue layout: ┌──────────────────────────────────────────┐ │ NAV: [Home] [About] [Contact] │ ← RouterLink ├──────────────────────────────────────────┤ │ │ │ <RouterView /> │ │ (shows HomeView, AboutView, or │ │ ContactView based on URL) │ │ │ └──────────────────────────────────────────┘ URL = "/" → RouterView renders HomeView URL = "/about" → RouterView renders AboutView URL = "/contact" → RouterView renders ContactView
RouterLink — Navigation Without Page Reload
<RouterLink> renders as an <a> tag but intercepts the click and updates the URL without reloading the page. It also adds an active class automatically when the current URL matches its to prop.
<RouterLink to="/about">About Us</RouterLink> <!-- Renders as: <a href="/about">About Us</a> --> <!-- When the URL is /about: --> <!-- <a href="/about" class="router-link-active">About Us</a> -->
Dynamic Routes — URL Parameters
Dynamic segments in a route path start with a colon. They capture a value from the URL and make it available to the component.
// router/index.js
const routes = [
{ path: "/user/:id", component: UserView },
{ path: "/product/:slug", component: ProductView }
];
<!-- These all match /user/:id --> <RouterLink to="/user/42">User 42</RouterLink> <RouterLink to="/user/99">User 99</RouterLink>
Accessing the Route Parameter
<!-- UserView.vue -->
<template>
<h2>User Profile: {{ userId }}</h2>
</template>
<script setup>
import { useRoute } from "vue-router";
const route = useRoute();
const userId = route.params.id; // "42" or "99" etc.
</script>
Diagram: Dynamic Route Matching
Route definition: /user/:id URL /user/42 → UserView loads, route.params.id = "42" URL /user/99 → UserView loads, route.params.id = "99" URL /user/sam → UserView loads, route.params.id = "sam"
Nested Routes
Routes can be nested inside parent routes. A child route renders inside the parent component's own <RouterView>.
const routes = [
{
path: "/dashboard",
component: DashboardView,
children: [
{ path: "", component: DashboardHome },
{ path: "stats", component: DashboardStats },
{ path: "settings", component: DashboardSettings }
]
}
];
Diagram: Nested Route Rendering
URL: /dashboard ┌──────────────────────────────────────────┐ │ DashboardView │ │ [Home] [Stats] [Settings] ← links │ │ ┌────────────────────────┐ │ │ │ <RouterView /> │ │ │ │ renders DashboardHome │ │ │ └────────────────────────┘ │ └──────────────────────────────────────────┘ URL: /dashboard/stats ┌──────────────────────────────────────────┐ │ DashboardView │ │ [Home] [Stats] [Settings] │ │ ┌────────────────────────┐ │ │ │ <RouterView /> │ │ │ │ renders DashboardStats │ │ │ └────────────────────────┘ │ └──────────────────────────────────────────┘
Programmatic Navigation
Navigate from JavaScript (not just links) using the router object.
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
function goToProfile(userId) {
router.push("/user/" + userId);
}
function goBack() {
router.back();
}
function goHome() {
router.push({ path: "/" });
}
// Named route navigation
function goToProduct(slug) {
router.push({ name: "product", params: { slug } });
}
</script>
Named Routes
Give routes a name so you can navigate to them by name instead of hardcoded paths. This makes refactoring easier — if the path changes, only the route definition needs updating.
const routes = [
{ path: "/product/:slug", name: "product", component: ProductView },
{ path: "/user/:id", name: "user", component: UserView }
];
<RouterLink :to="{ name: 'user', params: { id: 42 } }">
View User
</RouterLink>
Route Guards — Protecting Pages
Route guards run before a navigation happens. Use them to restrict access to certain routes — for example, preventing unauthenticated users from reaching a dashboard.
// Global guard — runs before every route change
router.beforeEach((to, from, next) => {
const isLoggedIn = localStorage.getItem("token");
if (to.meta.requiresAuth && !isLoggedIn) {
next("/login"); // redirect to login page
} else {
next(); // allow navigation
}
});
// Mark protected routes with meta
const routes = [
{ path: "/dashboard", component: Dashboard, meta: { requiresAuth: true } },
{ path: "/login", component: Login }
];
Diagram: Guard Decision Flow
User navigates to /dashboard
│
▼
beforeEach guard runs
│
requiresAuth = true
isLoggedIn = false?
│
Yes │ No
│ └──▶ next() → /dashboard loads ✓
▼
next("/login")
→ Redirected to /login
404 — Catch-All Route
Add a catch-all route at the end of your routes array to show a "not found" page for any unmatched URL.
const routes = [
{ path: "/", component: HomeView },
{ path: "/about", component: AboutView },
// Must be last:
{ path: "/:pathMatch(.*)*", name: "not-found", component: NotFoundView }
];
Summary
Vue Router maps URLs to components and swaps them into the <RouterView> without page reloads. Define routes as path-to-component pairs and register the router with app.use(router). Use <RouterLink> for navigation links. Dynamic segments like :id capture URL values accessible through route.params. Nest routes to build multi-level layouts. Navigate from code using router.push(). Protect routes with guards in beforeEach(). Use a catch-all route to handle unknown URLs gracefully.
