Skip to content
advanced Phase 9 · Routing with Vue Router

Navigation Guards

Run global and per-route checks for auth, confirmations, and scrolling.

50m
0 problems
Topic Progress 0%

Navigation Guards: Core Concepts

A guard controls a navigation transition

Vue Router navigation guards run around a route change and can allow it, redirect somewhere else, or cancel it. A global beforeEach guard is useful for policy that applies across many routes, such as directing a signed-out user to a login view. A beforeEnter guard belongs to a route record and is appropriate for a rule specific to that destination. In Vue Router 4, guards can return true or nothing to continue, false to cancel, or a route location to redirect. Prefer this return-value style over callback-based next code, which is easier to call more than once or forget.

Global guards receive the target route and the current route. The target can include matched records, params, query values, and route metadata. A common pattern marks protected records with a requiresAuth meta field and checks the merged target metadata. Return a login location when the user is not authenticated, and preserve a safe destination so the app can continue after login. The login flow must validate any return destination as an internal path; never redirect to arbitrary user-supplied external URLs.

Match guard scope to policy

beforeEach runs for every navigation and is appropriate for cross-cutting decisions. beforeEnter runs only when entering its route record; changing a param or query while staying on the same record does not necessarily re-enter that record. If access depends on the current resource id, ensure the policy reacts to param changes or is enforced by the data request. In-component guards can protect work such as unsaved edits, while a global guard can centralize broad authentication flow.

Route metadata is a convenient place to declare policy intent, but it should not become an untyped authorization database. Keep values small and declarative, such as requiresAuth or a permission key. The guard can consult an auth service or permission store. Avoid importing view components into a guard or duplicating business rules across route records; the goal is to decide whether navigation should proceed, not to implement the protected feature there.

Redirect, cancel, and continue

Returning false cancels the navigation and leaves the current location active. This is useful when a person rejects an unsaved-changes confirmation. Returning a location redirects, which is useful for login, canonical paths, or a missing-resource page. Returning true or no value continues. A guard that redirects to a location it also protects can create a loop; exempt the login route or ensure the target policy is satisfied after the redirect.

Use a per-route beforeEnter when the rule applies only upon entering that record. It may receive the same to and from context as a global guard, but its scope makes ownership clearer. A global beforeEach should remain short and predictable because it is on every navigation path. Async guards can wait for a session check, but they should have timeout and failure behavior; a stalled auth request should not leave the interface indefinitely unable to navigate.

Guards are navigation UX, not access control

A client route guard can hide a view from an ordinary navigation path, but it cannot protect data or operations. A user can call an API directly or alter client code. Every protected server endpoint must authenticate the request and authorize the requested resource. The guard improves flow and prevents confusing transitions; it is not a security boundary.

Navigation can fail for reasons beyond a guard returning false: a guard can throw, an async check can reject, or a lazy route component can fail to load. Handle these failures deliberately with router error hooks and useful logging. Do not convert all failures into a silent redirect to the home page, which erases context and makes defects hard to diagnose. A route policy should have a clear result for authenticated, unauthenticated, unauthorized, and unavailable cases.

Navigation Guards: Practice

Protect a route with a global policy

The example uses route metadata to express that selected destinations require a signed-in user. The global guard checks the current session, redirects to a named login route when needed, and carries the requested path as a return hint. It does not treat the route decision as proof that the user may access the server resource.

const routes = [
  { path: '/login', name: 'login', component: LoginView },
  {
    path: '/billing',
    name: 'billing',
    component: BillingView,
    meta: { requiresAuth: true },
  },
]

router.beforeEach(async (to) => {
  if (!to.meta.requiresAuth) return true

  const session = await authStore.restoreSession()
  if (session.user) return true

  return {
    name: 'login',
    query: { redirect: to.fullPath },
  }
})

At login completion, validate the redirect hint before navigating. A destination derived from the current internal route is generally safer than accepting arbitrary query input, but the login page can itself be opened with a crafted redirect parameter. Allow only known internal path forms or named destinations, reject protocol-relative and external URLs, and provide a safe default. This avoids turning a convenient return path into an open redirect.

The meta key is a declaration of navigation experience. It does not establish whether a user has the billing permission or owns the requested account. The view may hide controls based on permissions, but the API must make the final authorization decision. Handle 401 and 403 responses from data requests even when the guard passed, because sessions expire and permissions can change after navigation.

Add a destination-specific rule

If only one route needs an additional check, use beforeEnter on that record or a permission key that the global guard understands. Keep policy data declarative and load the current user or permission set through one auth boundary. Avoid repeating string comparisons in every component. A guard should return a stable location when access is denied and should not partially mutate feature state before deciding whether navigation can proceed.

An unsaved-changes guard has different semantics. It may ask the user to confirm leaving a dirty editor and return false if the user chooses to stay. The component owns the dirty state, so a component-level guard can be the clearest place for this decision. Clean forms should allow navigation immediately. Test both browser Back and an in-app link because navigation can begin from either source.

Exercise the guard outcomes

Test an anonymous user visiting a protected route, an authenticated user visiting it, and an anonymous user visiting the login route directly. Confirm the login route does not redirect to itself. After login, return to a safe internal destination and verify the route guard completes without a loop. Test an authorization denial separately from missing authentication; the user may need a forbidden page rather than a login form.

Return false from a confirmation guard and verify the current URL and view remain unchanged. Throw or reject from an asynchronous check and verify the application logs the failure and displays a recoverable state. Test an expired session while already on a protected route by making an API request, since navigation may not occur at the moment the session expires. The server response still needs a path back to authentication.

Guards are async-capable, but avoid making every route wait on a full profile request if a compact session state is sufficient. Cache stable session information, refresh it according to the auth design, and handle timeout or offline behavior explicitly. Do not trust a stale client permission cache for an operation; it only influences navigation and rendering. The protected endpoint must check current authorization.

Use a small test matrix and keep each guard's responsibility obvious. Global auth policy, route-specific permission, unsaved edits, and server authorization solve different problems. Separating them prevents a large global guard from becoming an untestable collection of feature rules.

Navigation Guards: Trade-offs and Interviews

Select the right guard boundary

Global beforeEach is powerful because it runs for every navigation, but that also makes it a high-impact boundary. Keep it focused on cross-cutting navigation policy such as session restoration or a redirect for protected routes. A route-specific beforeEnter makes an exceptional destination rule visible next to its record. A component guard is better for state owned by that component, such as unsaved edits. Avoid putting all policy in one global function simply because it can see every destination.

Understand the guard lifecycle when behavior depends on re-entry. A beforeEnter guard does not run merely because a param or query changes while the same route record remains active. If authorization depends on a resource id, enforce it in the data/service boundary and respond to param changes. Do not rely on navigation guards to catch all changes to application state or permissions.

Redirect loops and failure paths

Every redirect should have a termination condition. A protected route can redirect to login, but login itself must be exempt. A user with an active session may be redirected away from login, but ensure that destination is valid. Return locations should be validated to prevent external redirects. If a policy check fails due to network or service failure, distinguish that from a clear authorization denial; sending every failure to login can hide an outage or create loops.

Async guards may delay navigation. Use them when the decision truly needs an asynchronous result, such as restoring a session or loading a policy. Prefer a bounded request and a recoverable error state. Avoid repeating the same profile fetch on every route transition if a shared session store can provide current status. Session caches still have freshness limits and must not replace server checks.

Security and accessibility boundaries

A client-side guard is not an access-control mechanism. It can prevent a confusing page transition, but protected APIs must validate identity, permissions, and resource ownership on every request. A user can invoke an endpoint directly or manipulate JavaScript. Do not include secrets or authorization decisions that the server has not verified in route metadata. Treat metadata as client-visible configuration.

Redirects and cancellations should preserve a usable interface. If a guard cancels navigation, keep focus and current form values intact. If a session expires, explain what happened and allow the user to authenticate without losing a safe return destination. A forbidden page should state that access is unavailable without exposing sensitive resource details. Good routing behavior includes clear feedback, not merely a different URL.

Testing and interview framing

Test each outcome: continue, redirect, cancel, and thrown failure. Include a loop check, a crafted external return URL, a param change on the same record, and a rejected session request. Verify the protected API independently. Browser tests should cover Back navigation for unsaved changes and the full login return journey. These tests reveal whether the chosen guard scope matches the actual navigation lifecycle.

In an interview, explain global versus route-specific versus component guards, then identify the lifecycle case where beforeEnter does not rerun for a param-only change. Describe return-value outcomes and one redirect-loop defense. Most importantly, distinguish navigation UX from server authorization. That answer shows a guard is a controlled transition policy, not a security wall around client code.

Quiz

1. Which statement about Navigation Guards is accurate?

Question 1 options

2. When reviewing Navigation Guards, which point must hold?

Question 2 options

3. What does returning false from a Vue Router navigation guard do?

Question 3 options

4. What is a navigation guard not able to replace?

Question 4 options

Flashcards

Question

What is Navigation Guards?

Answer

Run global and per-route checks for auth, confirmations, and scrolling.

Question

Navigation Guards: why it matters

Answer

beforeEach runs before every navigation and can redirect by returning a new location.

Question

Navigation Guards: key detail

Answer

Returning false from a guard cancels the navigation entirely without an error.

Question

Navigation Guards: practical note

Answer

Per-route beforeEnter guards run only for that specific route record.

Question

Can a client navigation guard authorize protected data?

Answer

No; the server must authenticate and authorize every protected request.

Revision Notes

Key Takeaways

  • 1. beforeEach runs before every navigation and can redirect by returning a new location.
  • 2. Returning false from a guard cancels the navigation entirely without an error.
  • 3. Per-route beforeEnter guards run only for that specific route record.

Interview Tips

  • • Explain Navigation Guards with one concrete example from real vue work, not a textbook restatement.
  • • Discuss the trade-off: when Navigation Guards helps and when a lighter approach wins.
  • • Place Navigation Guards inside the Routing with Vue Router phase — interviewers check that you see the progression, not isolated facts.

Cheat Sheet

Navigation Guards Cheat Sheet

Phase: Routing with Vue Router | Level: advanced

Key points:

  • beforeEach runs before every navigation and can redirect by returning a new location.
  • Returning false from a guard cancels the navigation entirely without an error.
  • Per-route beforeEnter guards run only for that specific route record.
  • Guards receive the target and current routes so they can compare where the user is going.

Tags: vue, router, guards

Continue Learning

Before You Start

  • No prerequisites listed.

Next Topics

Related Reading