
Vue Router Best Practices
Avoid Vue Router 4 navigation-guard pitfalls, param lifecycle bugs, and redirect loops while shipping a production SPA.
Overview
Vue Router Best Practices is an agent skill for the Build phase that surfaces Vue Router 4 navigation-guard patterns, param lifecycle fixes, and production routing gotchas.
Install
npx skills add https://github.com/hyf0/vue-skills --skill vue-router-best-practicesWhat is this skill?
- Navigation guard recipes: async/await, deprecated next(), and infinite-loop prevention
- beforeRouteEnter constraints when you need component instance access
- Same-route param changes without remount—stale data fixes
- Listener cleanup patterns when leaving routes
- Production SPA setup guidance via vue-router
Adoption & trust: 2.1k installs on skills.sh; 2.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Vue SPA breaks on navigation—infinite redirects, stale route params, or guards that race API calls— and stack-overflow answers disagree on Router 4 patterns.
Who is it for?
Indie developers on Vue 3 SPAs who hit guard/async, same-route param updates, or listener leaks and want symptom-linked fixes during implementation.
Skip if: Greenfield framework choice (Nuxt vs Vue Router), non-Vue routers, or teams that only need static marketing pages without client routing.
When should I use this skill?
Building or debugging Vue Router 4 navigation, guards, route params, or route-component lifecycle in a Vue SPA.
What do I get? / Deliverables
Your agent applies documented guard, param-change, and cleanup patterns so navigation auth and data loading behave predictably in production routing.
- Guard and lifecycle fixes aligned to reference patterns
- Cleanup for route-mounted listeners
Recommended Skills
Journey fit
Routing belongs in Build when you are implementing client-side navigation for a Vue single-page app. Frontend is the canonical shelf because the skill addresses route components, guards, and SPA lifecycle—not backend APIs.
How it compares
Symptom-indexed Router 4 gotchas for agents—not a generic JavaScript routing tutorial or a component UI kit.
Common Questions / FAQ
Who is vue-router-best-practices for?
Solo and small-team frontend builders using Vue Router 4 in production SPAs who want an agent to apply vetted navigation and lifecycle patterns.
When should I use vue-router-best-practices?
Use it in the Build frontend phase while implementing auth guards, dynamic routes, and route-level data loading—or when debugging redirect loops and stale views after param-only navigation.
Is vue-router-best-practices safe to install?
It is documentation-style guidance without required destructive tools; review the Security Audits panel on this page and treat linked reference files like any third-party repo content.
SKILL.md
READMESKILL.md - Vue Router Best Practices
Vue Router best practices, common gotchas, and navigation patterns. ### Navigation Guards - Navigating between same route with different params → See [router-beforeenter-no-param-trigger](reference/router-beforeenter-no-param-trigger.md) - Accessing component instance in beforeRouteEnter guard → See [router-beforerouteenter-no-this](reference/router-beforerouteenter-no-this.md) - Navigation guard making API calls without awaiting → See [router-guard-async-await-pattern](reference/router-guard-async-await-pattern.md) - Users trapped in infinite redirect loops → See [router-navigation-guard-infinite-loop](reference/router-navigation-guard-infinite-loop.md) - Navigation guard using deprecated next() function → See [router-navigation-guard-next-deprecated](reference/router-navigation-guard-next-deprecated.md) ### Route Lifecycle - Stale data when navigating between same route → See [router-param-change-no-lifecycle](reference/router-param-change-no-lifecycle.md) - Event listeners persisting after component unmounts → See [router-simple-routing-cleanup](reference/router-simple-routing-cleanup.md) ### Setup - Building production single-page application → See [router-use-vue-router-for-production](reference/router-use-vue-router-for-production.md) --- title: Per-Route beforeEnter Guards Ignore Param/Query Changes impact: MEDIUM impactDescription: Route-level beforeEnter guards don't fire when only params, query, or hash change, causing unexpected bypasses of validation logic type: gotcha tags: [vue3, vue-router, navigation-guards, params, query] --- # 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 ```javascript // 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 ```vue <!-- 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 ```javascript // router.js router.beforeEach(async (to, from) => { // Handle all order access checks globally if (to.name === 'OrderDetail') {