Vue cookbook · Beginner

Protect routes with Vue Router navigation guards

Logged-out users can open account pages, and after login they lose the page they wanted.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Mark routes with meta.requiresAuth, check the session in a global beforeEach guard, redirect to login with the original path, and return there after logging in.

Solution

// router.ts
import { createRouter, createWebHistory } from "vue-router";
import { useSession } from "./stores/session";

declare module "vue-router" {
  interface RouteMeta {
    requiresAuth?: boolean;
    role?: "admin";
  }
}

export const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: "/", component: () => import("./pages/Home.vue") },
    { path: "/login", component: () => import("./pages/Login.vue") },
    { path: "/account", component: () => import("./pages/Account.vue"), meta: { requiresAuth: true } },
    { path: "/admin", component: () => import("./pages/Admin.vue"), meta: { requiresAuth: true, role: "admin" } },
  ],
});

router.beforeEach(async (to) => {
  const session = useSession();
  if (!session.loaded) await session.load();               // e.g. GET /api/me once
  if (to.meta.requiresAuth && !session.user) {
    return { path: "/login", query: { redirect: to.fullPath } };
  }
  if (to.meta.role && session.user?.role !== to.meta.role) return { path: "/" };
});

Pitfalls

  • Treating route guards as security; the API must check permissions on every request.
  • Accepting any redirect query value after login (open redirect); only allow paths starting with /.
  • Calling /api/me on every navigation; load once and cache in the store.

Variations

  • Per-route beforeEnter guards for one-off checks.
  • In Nuxt, use route middleware (definePageMeta({ middleware: "auth" })).

Further reading: Navigation guards

Reference: https://router.vuejs.org/guide/advanced/navigation-guards.html

More Vue recipes

Was this page helpful?