All skills
hyf0 avatar

/vue-router-best-practices

@bc922e4 official
by hyf0hyf0/vue-skills2.9k stars
167

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

Use this Skill: https://skilld.dev/gh/hyf0/vue-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

README badge

README badge for hyf0/vue-skills/vue-router-best-practices

Teaches Vue Router 4 navigation guards, route lifecycle patterns, and common gotchas like stale data when params change and infinite redirect loops. Covers async/await in guards, beforeRouteEnter access patterns, and cleanup on component unmount.

Generated from the current SKILL.md.

Does this cover Vue Router 3 or Vue Router 4?
This skill targets Vue Router 4 specifically. It covers patterns and navigation guards in Vue 4's composition API context.
What are the main topics covered?
Navigation guards (beforeRouteEnter, beforeEach, infinite loops, async patterns), route lifecycle gotchas (stale data, cleanup), and production single-page app setup.
Does this address the deprecated next() function?
Yes. The skill includes guidance on replacing deprecated next() callbacks with modern return-based patterns in Vue Router 4.
Will this help with same-route navigation issues?
Yes. It covers both navigating to the same route with different params and stale data problems when component lifecycle hooks don't trigger on param changes.

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