Route Guards SPA+
Per-route leave guards, metadata inheritance, and navigation cancellation. For basic guard setup with beforeEnter and beforeEach, see Route Management.
beforeLeave Guards
Per-route leave guards run when navigating away from a route. Use them to protect against accidental data loss.
Basic Usage
router.onRoute('/editor', {
handler: () => showEditor(),
beforeLeave: ({ to, from }) => {
if (hasUnsavedChanges()) {
return false; // Block navigation
}
return true; // Allow navigation
}
});
Redirect on Leave
router.onRoute('/checkout', {
handler: () => showCheckout(),
beforeLeave: ({ to }) => {
// Must confirm before leaving checkout
if (!orderConfirmed && to.path !== '/confirm-leave') {
return '/confirm-leave'; // Redirect instead
}
return true;
}
});
Guard Return Values
| Return Value | Effect |
|---|---|
true / undefined | Allow leaving the route |
false | Block navigation, stay on current route |
'/path' | Redirect to a different path |
Route Metadata Inheritance
Child routes inherit their parent's meta properties. Child values override parent values on collision.
const router = wildflower.createRouter({
routes: [
{
path: '/admin',
meta: { requiresAuth: true, layout: 'admin' },
handler: showAdminHome,
children: [
{
path: '/users',
meta: { requiresAdmin: true },
handler: showAdminUsers
// Effective meta: { requiresAuth: true, layout: 'admin', requiresAdmin: true }
},
{
path: '/settings',
meta: { layout: 'settings' }, // Overrides parent layout
handler: showSettings
// Effective meta: { requiresAuth: true, layout: 'settings' }
}
]
}
]
});
// Use in a global guard
router.beforeEach(({ to }) => {
if (to.meta.requiresAuth && !isLoggedIn()) {
return '/login';
}
if (to.meta.requiresAdmin && !isAdmin()) {
return '/unauthorized';
}
return true;
});
Navigation Cancellation
Cancel an in-progress navigation from outside the guard chain using abortNavigation():
// Start a navigation
router.navigate('/slow-page');
// Cancel it before it completes
router.abortNavigation();
// Practical example: timeout protection
router.navigate('/api-data');
const timeout = setTimeout(() => {
if (router.isNavigating) {
router.abortNavigation();
showTimeoutError();
}
}, 5000);
abortNavigation() only works while router.isNavigating is true. It has no effect after navigation completes.