All skills
vuejs-ai avatar

/vue-router-best-practices

@bc922e4 official
by Vue AIvuejs-ai/skills2.9k stars
167

Vue Router 4 patterns, navigation guards, route params, and route-component lifecycle interactions.

Use this Skill: https://skilld.dev/gh/vuejs-ai/skills/vue-router-best-practices

This session only. Nothing lands on disk.

referencerouter-navigation-guard-infinite-loop.md

≈1.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Navigation Guard Infinite Redirect Loops

Impact: HIGH - A common mistake in navigation guards is creating conditions that cause infinite redirects. Vue Router will detect this and show a warning, but in production, it can crash the browser or create a broken user experience.

Task Checklist

  • Always check if already on target route before redirecting
  • Test guard logic with all possible navigation scenarios
  • Add route meta to control which routes need protection
  • Use Vue Router devtools to debug redirect chains

The Problem

// WRONG: Infinite loop - always redirects to login, even when on login!
router.beforeEach((to, from) => {
  if (!isAuthenticated()) {
    return '/login'  // Redirects to /login, which triggers guard again...
  }
})

// WRONG: Circular redirect between two routes
router.beforeEach((to, from) => {
  if (to.path === '/dashboard' && !hasProfile()) {
    return '/profile'
  }
  if (to.path === '/profile' && !isVerified()) {
    return '/dashboard'  // Back to dashboard, which goes to profile...
  }
})

Error you'll see:

[Vue Router warn]: Detected an infinite redirection in a navigation guard when going from "/" to "/login". Aborting to avoid a Stack Overflow.

Solution 1: Exclude Target Route

// CORRECT: Don't redirect if already going to login
router.beforeEach((to, from) => {
  if (!isAuthenticated() && to.path !== '/login') {
    return '/login'
  }
})

// CORRECT: Use route name for cleaner check
router.beforeEach((to, from) => {
  const publicPages = ['Login', 'Register', 'ForgotPassword']

  if (!isAuthenticated() && !publicPages.includes(to.name)) {
    return { name: 'Login' }
  }
})

Solution 2: Use Route Meta Fields

// router.js
const routes = [
  {
    path: '/login',
    name: 'Login',
    component: Login,
    meta: { requiresAuth: false }
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: Dashboard,
    meta: { requiresAuth: true }
  },
  {
    path: '/public',
    name: 'PublicPage',
    component: PublicPage,
    meta: { requiresAuth: false }
  }
]

// Guard checks meta field
router.beforeEach((to, from) => {
  // Only redirect if route requires auth
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return { name: 'Login', query: { redirect: to.fullPath } }
  }
})

Solution 3: Handle Redirect Chains Carefully

// CORRECT: Break potential circular redirects
router.beforeEach((to, from) => {
  // Prevent redirect loops by tracking redirect depth
  const redirectCount = to.query._redirectCount || 0

  if (redirectCount > 3) {
    console.error('Too many redirects, stopping at:', to.path)
    return '/error'  // Escape hatch
  }

  if (needsRedirect(to)) {
    return {
      path: getRedirectTarget(to),
      query: { ...to.query, _redirectCount: redirectCount + 1 }
    }
  }
})

Solution 4: Centralized Redirect Logic

// guards/auth.js
export function createAuthGuard(router) {
  const publicRoutes = new Set(['Login', 'Register', 'ForgotPassword', 'ResetPassword'])
  const guestOnlyRoutes = new Set(['Login', 'Register'])

  router.beforeEach((to, from) => {
    const isPublic = publicRoutes.has(to.name)
    const isGuestOnly = guestOnlyRoutes.has(to.name)
    const isLoggedIn = isAuthenticated()

    // Not logged in, trying to access protected route
    if (!isLoggedIn && !isPublic) {
      return { name: 'Login', query: { redirect: to.fullPath } }
    }

    // Logged in, trying to access guest-only route (like login page)
    if (isLoggedIn && isGuestOnly) {
      return { name: 'Dashboard' }
    }

    // All other cases: proceed
  })
}

Debugging Redirect Loops

// Add logging to understand the redirect chain
router.beforeEach((to, from) => {
  console.log(`Navigation: ${from.path} -> ${to.path}`)
  console.log('Auth state:', isAuthenticated())
  console.log('Route meta:', to.meta)

  // Your guard logic here
})

// Or use afterEach for confirmed navigations
router.afterEach((to, from) => {
  console.log(`Navigated: ${from.path} -> ${to.path}`)
})

Common Redirect Loop Patterns

Pattern Problem Fix
Auth check without exclusion Login redirects to login Exclude /login from check
Role-based with circular deps Admin -> User -> Admin Use single source of truth for role requirements
Onboarding flow Step 1 -> Step 2 -> Step 1 Track completion state properly
Redirect query handling Reading redirect creates new redirect Process redirect only once

Key Points

  1. Always exclude the target route - Never redirect to a route that would trigger the same redirect
  2. Use route meta fields - Cleaner than path string comparisons
  3. Test edge cases - Direct URL access, refresh, back button
  4. Add logging during development - Helps trace redirect chains
  5. Have an escape hatch - Error page or max redirect count

Reference

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill is a collection of educational documentation and best practices for using Vue Router 4. It contains no executable code, network operations, or sensitive data access, and is focused entirely on providing guidance for frontend development patterns.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    9 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at bc922e4. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 8 months ago
version
1.0.0
author
github.com/vuejs-ai
  • Vue
  • vue-router
  • navigation-guards
  • routing
  • lifecycle
  • spa

README badge

README badge for vuejs-ai/skills/vue-router-best-practices

Teaches Vue Router 4 navigation guards, route lifecycle handling, and common patterns like preventing infinite redirects and managing stale data across param changes. Covers gotchas like component cleanup on navigation and the deprecated next() function.

Generated from the current SKILL.md.

Does this skill cover Vue Router 4?
Yes. The skill is specific to Vue Router 4 patterns, navigation guards, route params, and component lifecycle interactions.
What are the main topics covered?
Navigation guards (async/await, infinite loops, deprecated next() function), route lifecycle issues (stale data, cleanup), and production SPA setup.
Does this address the problem of component data not updating when route params change?
Yes. The skill includes guidance on handling stale data when navigating between the same route with different params.
Does this cover how to access component instances in navigation guards?
Yes. It addresses the gotcha that `this` is not available in beforeRouteEnter guards and explains the workaround.
Does this skill help prevent infinite redirect loops?
Yes. It includes patterns for identifying and avoiding infinite navigation guard redirect loops.

Generated from the current SKILL.md. These answers refresh after source changes.