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-beforeenter-no-param-trigger.md

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

Per-Route beforeEnter Guards Ignore Param/Query Changes

Impact: MEDIUM - The beforeEnter guard defined in route configuration only triggers when entering a route from a DIFFERENT route. Changes to params, query strings, or hash within the same route do NOT trigger beforeEnter, potentially bypassing important validation logic.

Task Checklist

  • Use in-component onBeforeRouteUpdate for param/query changes
  • Or use global beforeEach with route.params/query checks
  • Document which guards protect which scenarios
  • Test navigation between same route with different params

The Problem

// router.js
const routes = [
  {
    path: '/orders/:id',
    component: OrderDetail,
    beforeEnter: async (to, from) => {
      // This runs when entering from /products
      // But NOT when navigating from /orders/1 to /orders/2!
      const order = await checkOrderAccess(to.params.id)
      if (!order.canView) {
        return '/unauthorized'
      }
    }
  }
]

Scenario:

  1. User navigates from /products to /orders/1 - beforeEnter runs, access checked
  2. User navigates from /orders/1 to /orders/2 - beforeEnter DOES NOT run!
  3. User might access order they don't have permission for!

What Triggers beforeEnter vs. What Doesn't

Navigation beforeEnter fires?
/products → /orders/1 YES
/orders/1 → /orders/2 NO
/orders/1 → /orders/1?tab=details NO
/orders/1#section → /orders/1#other NO
/orders/1 → /products → /orders/2 YES (leaving and re-entering)

Solution 1: Add In-Component Guard

<!-- OrderDetail.vue -->
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'

// Handle param changes within the same route
onBeforeRouteUpdate(async (to, from) => {
  if (to.params.id !== from.params.id) {
    const order = await checkOrderAccess(to.params.id)
    if (!order.canView) {
      return '/unauthorized'
    }
  }
})
</script>

Solution 2: Use Global beforeEach Instead

// router.js
router.beforeEach(async (to, from) => {
  // Handle all order access checks globally
  if (to.name === 'OrderDetail') {
    // This runs on EVERY navigation to this route, including param changes
    const order = await checkOrderAccess(to.params.id)
    if (!order.canView) {
      return '/unauthorized'
    }
  }
})

Solution 3: Combine Both Guards

// router.js - For entering from different route
const routes = [
  {
    path: '/orders/:id',
    component: OrderDetail,
    beforeEnter: (to) => validateOrderAccess(to.params.id)
  }
]

// In component - For param changes within route
// OrderDetail.vue
onBeforeRouteUpdate((to) => validateOrderAccess(to.params.id))

// Shared validation function
async function validateOrderAccess(orderId) {
  const order = await checkOrderAccess(orderId)
  if (!order.canView) {
    return '/unauthorized'
  }
}

Solution 4: Use beforeEnter with Array of Guards

// guards/orderGuards.js
export const orderAccessGuard = async (to) => {
  const order = await checkOrderAccess(to.params.id)
  if (!order.canView) {
    return '/unauthorized'
  }
}

// router.js
const routes = [
  {
    path: '/orders/:id',
    component: OrderDetail,
    beforeEnter: [orderAccessGuard]  // Can add multiple guards
  }
]

// Still need in-component guard for param changes!

Full Navigation Guard Execution Order

Understanding when each guard type fires:

1. beforeRouteLeave (in-component, leaving component)
2. beforeEach (global)
3. beforeEnter (per-route, ONLY when entering from different route)
4. beforeRouteEnter (in-component, entering component)
5. beforeResolve (global)
6. afterEach (global, after navigation confirmed)

For param/query changes on same route:
1. beforeRouteUpdate (in-component) - ONLY this fires!
2. beforeEach (global)
3. beforeResolve (global)
4. afterEach (global)

Key Points

  1. beforeEnter is for route ENTRY only - Not for within-route changes
  2. Use onBeforeRouteUpdate for param changes - This is the in-component solution
  3. Global beforeEach always runs - Good for centralized validation
  4. Test param change scenarios - Easy to miss during development
  5. Consider security implications - Param-based access control needs both guards

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.