NavigationGuard
Navigation guard function type, used to perform validation, redirection, analytics, and other logic before/after navigation occurs.
Type Definition
type NavigationGuard = (
to: RouteLocationNormalized,
from: RouteLocationNormalized,
next: NavigationGuardNext
) => void | Promise<void | boolean | RouteLocationRaw>Parameters
| Parameter | Type | Description |
|---|---|---|
to | RouteLocationNormalized | The target route being navigated to |
from | RouteLocationNormalized | The current route being navigated away from |
next | NavigationGuardNext | Callback function that controls the navigation flow |
Return Value
Guards support two writing styles:
- Callback style: Control via
next() - Promise style: Return a value directly, the router handles it automatically
// Callback style
router.beforeEach((to, from, next) => {
if (isLoggedIn()) next()
else next({ name: 'login' })
})
// Promise style (recommended)
router.beforeEach(async (to, from) => {
if (isLoggedIn()) return true
return { name: 'login' }
})Recommended to use Promise style
- Better fits async/await style, cleaner code
- Avoids forgetting to call
next()causing navigation to hang - Exceptions automatically convert to
next(false), no try-catch needed
NavigationGuardNext
Callback function that controls the navigation flow, supporting multiple call forms:
type NavigationGuardNext = {
(): void // Allow
(valid: boolean): void // true=allow, false=abort
(location: RouteLocationRaw): void // Redirect
(location: RouteLocationRaw, options: NavigationGuardNextOptions): void // Redirect + options
(error: Error): void // Throw error
(cb: (vm: ComponentPublicInstance) => void): void // Access component instance
}Allow
next() // Callback style
return true // Promise styleAbort
Abort navigation, stay on the current page:
next(false) // Callback style
return false // Promise styleRedirect
Redirect to another route:
// Basic redirect (uses push mode by default)
next({ name: 'login' })
next('/pages/login/login')
next({ path: '/pages/login/login', query: { redirect: to.fullPath } })
// Promise style
return { name: 'login' }Redirect + Options (v1.7.0+)
Control the redirect mode via NavigationGuardNextOptions:
interface NavigationGuardNextOptions {
mode?: NavigationRedirectMode // 'push' | 'replace' | 'relaunch'
}
type NavigationRedirectMode = 'push' | 'replace' | 'relaunch'// Replace mode: don't keep current page in stack after redirect
next({ name: 'login' }, { mode: 'replace' })
// Relaunch mode: clear page stack
next({ name: 'home' }, { mode: 'relaunch' })
// Promise style (via return object)
return {
location: { name: 'login' },
mode: 'replace'
}mode use cases
push(default): Regular redirect, keeps current page in stackreplace: Login redirect (avoids returning to protected page), jump after form submissionrelaunch: Logout, switch user, return to home
See Guard Redirect Mode.
Throw Error
next(new Error('Insufficient permissions'))
// Promise style
throw new Error('Insufficient permissions')The error will be caught by router.onError and navigation will be aborted.
Access Component Instance
next((vm) => {
// vm is the target page component instance
vm.fetchData()
})Limitation
This form is only supported by the beforeRouteEnter guard (rarely used in uni-app because pages are independent components). beforeEach / beforeResolve / afterEach do not support it.
Guard Type Classification
Global Before Guard
const removeGuard = router.beforeEach((to, from, next) => {
// Permission validation, login check, analytics, etc.
if (to.meta.requireAuth && !isLoggedIn()) {
next({ name: 'login', query: { redirect: to.fullPath } }, { mode: 'replace' })
} else {
next()
}
})
// Remove guard
removeGuard()Global Resolve Guard
Executes after beforeEach and beforeEnter, commonly used to wait for async data loading to complete:
router.beforeResolve(async (to) => {
if (to.meta.preload) {
await store.preloadData(to.meta.preload)
}
return true
})Global After Hook
Executes after navigation completes, does not accept next parameter, cannot change navigation flow:
router.afterEach((to, from) => {
// Set title
if (to.meta.title) {
uni.setNavigationBarTitle({ title: to.meta.title as string })
}
// Page analytics
trackPageView(to.path)
})Route Exclusive Guard
Configured via RouteConfig.beforeEnter, only takes effect for that route:
const routes = [
{
path: 'pages/admin/admin',
name: 'admin',
beforeEnter: (to, from, next) => {
if (hasRole('admin')) next()
else next({ name: '403' })
}
}
]Execution Order
The guard execution order for a complete navigation:
1. beforeEach (global before guard, in registration order)
↓
2. beforeEnter (route exclusive guard, in array order)
↓
3. beforeResolve (global resolve guard, in registration order)
↓
4. Navigation confirmed, execute uni native jump
↓
5. afterEach (global after hook, in registration order)Behavior after guard abort
- Any guard returns
falseor throws an error: navigation aborts, subsequent guards don't execute - Any guard returns a redirect: restarts the full flow (from
beforeEach) afterEachis unaffected: only executes after navigation is confirmed, cannot abort
Promise Style Return Value
type GuardResult = void | boolean | RouteLocationRaw | {
location: RouteLocationRaw
mode?: NavigationRedirectMode
}| Return Value | Equivalent to next() | Description |
|---|---|---|
undefined / void | next() | Allow |
true | next() | Allow |
false | next(false) | Abort |
RouteLocationRaw | next(RouteLocationRaw) | Redirect (push) |
{ location, mode } | next(location, { mode }) | Redirect + mode control |
// Allow
router.beforeEach(() => {})
// Abort
router.beforeEach(() => false)
// Redirect (push)
router.beforeEach(() => ({ name: 'login' }))
// Redirect (replace)
router.beforeEach(() => ({
location: { name: 'login' },
mode: 'replace'
}))Practical Examples
Login Validation
router.beforeEach((to, from) => {
const isLoggedIn = !!uni.getStorageSync('token')
if (to.meta.requireAuth && !isLoggedIn) {
// Redirect to login page, use replace to avoid returning to protected page
return {
location: { name: 'login', query: { redirect: to.fullPath } },
mode: 'replace'
}
}
return true
})Permission Validation
// Type augmentation
declare module '@meng-xi/uni-router' {
interface RouteMeta {
roles?: string[]
}
}
router.beforeEach((to) => {
if (to.meta.roles) {
const userRoles = getUserRoles()
if (!to.meta.roles.some(r => userRoles.includes(r))) {
uni.showToast({ title: 'No permission', icon: 'none' })
return false
}
}
return true
})Async Data Preloading
router.beforeResolve(async (to) => {
if (to.name === 'detail') {
try {
await store.fetchDetail(to.query.id)
} catch (err) {
uni.showToast({ title: 'Load failed', icon: 'none' })
return false
}
}
return true
})Page Analytics
declare module '@meng-xi/uni-router' {
interface RouteMeta {
trackName?: string
}
}
router.afterEach((to, from) => {
if (to.meta.trackName) {
trackPageView(to.meta.trackName, {
from: from.path,
to: to.path,
duration: Date.now() - pageStartTime
})
}
pageStartTime = Date.now()
})Dynamic Title
router.afterEach((to) => {
const title = to.meta.title as string | undefined
uni.setNavigationBarTitle({ title: title || 'Default Title' })
})Prevent Duplicate Navigation
let isNavigating = false
router.beforeEach((to, from, next) => {
if (isNavigating) {
next(false)
return
}
isNavigating = true
next()
})
router.afterEach(() => {
isNavigating = false
})FAQ
What happens if I forget to call next()?
After the guard times out (default 10 seconds, configurable via guardTimeout), it automatically aborts navigation and outputs a warning:
[uni-router] Guard timeout after 10000ms, navigation abortedSolutions
- Use Promise style to avoid forgetting to call
next() - Adjust
guardTimeoutfor async operations - Check that all branches in the guard call
next()
Can I access the component instance in a guard?
beforeEach/beforeResolve: No, the target component hasn't been created yetafterEach: No, but you can get the page instance viagetCurrentPages()beforeRouteEnter: Can access vianext((vm) => {...})
What happens if an exception is thrown in a guard?
The exception will be caught by router.onError and the current navigation will be aborted:
router.onError((err, to, from) => {
console.error('Navigation error:', err)
uni.showToast({ title: 'Page load failed', icon: 'none' })
})
router.beforeEach(async (to) => {
if (to.meta.requireAuth) {
const user = await fetchUser() // May throw network error
if (!user) return { name: 'login' }
}
return true
})Next Steps
- Route Guards Guide — In-depth explanation of guards
- Router Instance — Methods for registering guards
- RouterError Type — Error handling
