Files
agent-skills/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

151 lines
4.2 KiB
Markdown

---
title: Vue Router Navigation Guard next() Function Deprecated
impact: HIGH
impactDescription: Using the deprecated next() function incorrectly causes navigation to hang, infinite loops, or silent failures
type: gotcha
tags: [vue3, vue-router, navigation-guards, migration, async]
---
# Vue Router Navigation Guard next() Function Deprecated
**Impact: HIGH** - The third `next()` argument in navigation guards is deprecated in Vue Router 4. While still supported for backward compatibility, using it incorrectly is one of the most common sources of bugs: calling it multiple times, forgetting to call it, or calling it conditionally without proper logic.
## Task Checklist
- [ ] Refactor guards to use return-based syntax instead of next()
- [ ] Remove all next() calls from navigation guards
- [ ] Use async/await pattern for asynchronous checks
- [ ] Return false to cancel, return route to redirect, return nothing to proceed
## The Problem
```javascript
// WRONG: Using deprecated next() function
router.beforeEach((to, from, next) => {
if (!isAuthenticated) {
next('/login') // Easy to forget this call
}
// BUG: next() not called when authenticated - navigation hangs!
})
// WRONG: Multiple next() calls
router.beforeEach((to, from, next) => {
if (!isAuthenticated) {
next('/login')
}
next() // BUG: Called twice when not authenticated!
})
// WRONG: next() in async code without proper handling
router.beforeEach(async (to, from, next) => {
const user = await fetchUser()
if (!user) {
next('/login')
}
next() // Still gets called even after redirect!
})
```
## Solution: Use Return-Based Guards
```javascript
// CORRECT: Return-based syntax (modern Vue Router 4+)
router.beforeEach((to, from) => {
if (!isAuthenticated) {
return '/login' // Redirect
}
// Return nothing (undefined) to proceed
})
// CORRECT: Return false to cancel navigation
router.beforeEach((to, from) => {
if (hasUnsavedChanges) {
return false // Cancel navigation
}
})
// CORRECT: Async with return-based syntax
router.beforeEach(async (to, from) => {
const user = await fetchUser()
if (!user) {
return { name: 'Login', query: { redirect: to.fullPath } }
}
// Proceed with navigation
})
```
## Return Values Explained
```javascript
router.beforeEach((to, from) => {
// Return nothing/undefined - allow navigation
return
// Return false - cancel navigation, stay on current route
return false
// Return string path - redirect to path
return '/login'
// Return route object - redirect with full control
return { name: 'Login', query: { redirect: to.fullPath } }
// Return Error - cancel and trigger router.onError()
return new Error('Navigation cancelled')
})
```
## If You Must Use next() (Legacy Code)
If maintaining legacy code that uses `next()`, follow these rules strictly:
```javascript
// CORRECT: Exactly one next() call per code path
router.beforeEach((to, from, next) => {
if (!isAuthenticated) {
next('/login')
return // CRITICAL: Exit after calling next()
}
if (!hasPermission(to)) {
next('/forbidden')
return // CRITICAL: Exit after calling next()
}
next() // Only reached if all checks pass
})
```
## Error Handling Pattern
```javascript
router.beforeEach(async (to, from) => {
try {
await validateAccess(to)
// Proceed
} catch (error) {
if (error.status === 401) {
return '/login'
}
if (error.status === 403) {
return '/forbidden'
}
// Log error and proceed anyway (or return false)
console.error('Access validation failed:', error)
return false
}
})
```
## Key Points
1. **Prefer return-based syntax** - Cleaner, less error-prone, modern standard
2. **next() must be called exactly once** - If using legacy syntax, ensure single call per path
3. **Always return/exit after redirect** - Prevent multiple navigation actions
4. **Async guards work naturally** - Just return the redirect route or nothing
5. **Test all code paths** - Each branch must result in either return or next()
## Reference
- [Vue Router Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html)
- [RFC: Remove next() from Navigation Guards](https://github.com/vuejs/rfcs/discussions/302)