SyntaxStudy
Sign Up
Vue.js Navigation Guards and Route Meta
Vue.js Beginner 1 min read

Navigation Guards and Route Meta

Vue Router's navigation guards let you intercept route changes to implement authentication, permission checks, analytics, or data prefetching. Global guards are registered on the router instance: `beforeEach` runs before every navigation, `afterEach` runs after each navigation (cannot cancel it), and `beforeResolve` runs after in-component guards and async components are resolved. Per-route guards defined in the route record (`beforeEnter`) run only for that specific route. In-component guards use `onBeforeRouteLeave` and `onBeforeRouteUpdate` composable functions. Calling `next(false)` or returning `false` cancels the navigation; returning a location object redirects. In Vue Router 4 you can simply `return` a value instead of calling `next`. Route `meta` fields attach arbitrary data to route records — typical uses include `meta.requiresAuth`, `meta.title`, or `meta.roles`. Meta fields from parent routes are inherited by children and merged via `route.meta`. A single global `beforeEach` guard can check `to.meta.requiresAuth` and redirect unauthenticated users to the login page without duplicating logic in each protected component.
Example
// src/router/index.ts (guards section)
import { createRouter, createWebHistory } from 'vue-router';
import { useAuthStore } from '@/stores/auth';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/login',     name: 'login',     component: () => import('../views/LoginView.vue') },
    { path: '/dashboard', name: 'dashboard', component: () => import('../views/DashboardView.vue'),
      meta: { requiresAuth: true, title: 'Dashboard' } },
    { path: '/admin',     name: 'admin',     component: () => import('../views/AdminView.vue'),
      meta: { requiresAuth: true, roles: ['admin'] } },
  ],
});

// Global before guard
router.beforeEach((to, from) => {
  const auth = useAuthStore();
  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return { name: 'login', query: { redirect: to.fullPath } };
  }
  if (to.meta.roles && !to.meta.roles.includes(auth.role)) {
    return { name: 'dashboard' }; // redirect unauthorised
  }
});

// Set document title
router.afterEach((to) => {
  document.title = to.meta.title ? `${to.meta.title} | MyApp` : 'MyApp';
});

export default router;