Vue Router

Chapter 11
Vue Router
Multiple pages in a single-page app — the official routing library, deeply familiar by now

Routing in Vue is its own official package (vue-router), like React Router was a separate install — but the concepts are now thoroughly familiar from both the React and Angular courses. Define routes mapping paths to components, render the matched one in an outlet, link without reloading, read params, navigate from code, and guard routes. Same ideas, Vue's API.

Defining Routes

// router/index.js (npm install vue-router) import { createRouter, createWebHistory } from 'vue-router'; import Home from '../views/Home.vue'; import About from '../views/About.vue'; const routes = [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/product/:id', component: ProductDetail }, { path: '/:pathMatch(.*)*', component: NotFound }, ]; export const router = createRouter({ history: createWebHistory(), routes, });

Routes are an array of { path, component } objects, the same shape as Angular's route config and React Router's <Route> list. :id is a dynamic segment; /:pathMatch(.*)* is Vue Router's (slightly cryptic) catch-all for unmatched URLs. createWebHistory() enables clean URLs (no #). The router is then registered in main.js with app.use(router) — the same plugin pattern Pinia will use next chapter.

RouterView and RouterLink

<!-- App.vue --> <template> <nav> <RouterLink to="/">Home</RouterLink> <RouterLink to="/about">About</RouterLink> </nav> <RouterView /> </template>

<RouterView /> is the outlet where the matched route's component renders — the equivalent of React Router's <Outlet /> and Angular's <router-outlet>. <RouterLink to="/about"> navigates without a full page reload, like React's <Link> and Angular's routerLink. A RouterLink automatically gets an active CSS class when its route is current, so highlighting the active nav item needs no manual URL comparison.

Reading Route Params with useRoute

<!-- ProductDetail.vue --> <script setup> import { useRoute } from 'vue-router'; const route = useRoute(); const id = route.params.id; </script> <template> <p>Showing product {{ route.params.id }}</p> </template>

useRoute() returns the current route object; route.params.id reads the :id segment — the equivalent of React Router's useParams() and Angular's ActivatedRoute.paramMap. The route object is reactive, so if the same component stays mounted while only the param changes (navigating /product/1/product/2), a watch (Chapter 5) on route.params.id reacts to that change.

Programmatic Navigation with useRouter

import { useRouter } from 'vue-router'; const router = useRouter(); function goToProduct(id) { router.push(`/product/${id}`); // or: router.push({ path: '/product/' + id }) }

useRouter() returns the router instance; router.push(...) navigates from code — after a form submits, a login succeeds, an action completes — the equivalent of React Router's useNavigate() and Angular's Router.navigate(). Note the two distinct hooks: useRoute (singular, the current route's data) vs useRouter (the router, for navigating) — an easy pair to mix up by name.

Navigation Guards

// a global guard, in router/index.js router.beforeEach((to, from) => { const isLoggedIn = checkAuth(); if (to.path === '/dashboard' && !isLoggedIn) { return '/login'; // redirect by returning a path } // return true or nothing to allow });

A navigation guard runs before each route change — beforeEach receives the target (to) and current (from) routes, and either allows navigation (return true or nothing) or redirects (return a path). This is Vue's equivalent of Angular's canActivate guard, and the standard way to keep unauthenticated users out of protected pages. Guards can also be defined per-route (beforeEnter) or inside a component.

Lazy-loading routes — code splitting built in
Like Angular's loadComponent, Vue Router supports lazy-loading a route's component so its code only downloads when the route is first visited: { path: '/admin', component: () => import('../views/Admin.vue') }. A dynamic import() as the component is all it takes — no separate Suspense boundary needed (the React equivalent from Advanced Chapter 3). This is the standard way to keep the initial bundle small.
RouterLink, not a plain anchor
Using <a href="/about"> instead of <RouterLink to="/about"> triggers a real full-page reload, throwing away all in-memory state — the same warning as React Router and Angular. RouterLink intercepts the click and updates the URL via the History API without a reload. Always use it for in-app navigation.
Vue RouterReact RouterAngular
routes array + createRouter<Routes>/<Route>routes + provideRouter
<RouterView /><Outlet /><router-outlet>
<RouterLink to><Link to>routerLink
useRoute().paramsuseParams()ActivatedRoute.paramMap
useRouter().push()useNavigate()Router.navigate()
beforeEach guardHand-rolled wrappercanActivate

Coding Challenges

Challenge 1

Set up a 3-page app (Home, About, Contact) with a routes array, a nav using RouterLink, a RouterView, and a catch-all route showing a NotFound component for unmatched URLs.

📄 View solution
Challenge 2

Add a /product/:id route. From a product list, use RouterLink to navigate to a detail page that reads the id via useRoute and displays the matching product.

📄 View solution
Challenge 3

Add a beforeEach navigation guard protecting a /dashboard route, redirecting to /login when a simple isLoggedIn flag is false, with a button toggling the flag to test both outcomes.

📄 View solution

Chapter 11 Quick Reference

  • createRouter + a routes array (path → component); register with app.use(router)
  • <RouterView /> renders the matched route; <RouterLink to> navigates without reload
  • :id dynamic segments; /:pathMatch(.*)* is the catch-all route
  • useRoute() — current route data (route.params.id); useRouter() — navigate (router.push)
  • beforeEach((to, from) => ...) — a navigation guard; return a path to redirect, true/nothing to allow
  • Lazy-load a route with component: () => import('...') — built-in code splitting
  • Always use RouterLink, never a plain <a href>, for in-app navigation
  • Next chapter: state management with Pinia, plus a data-fetching pattern (final chapter)