Built-in Permission System
The router includes a flexible permission system for implementing role-based access control (RBAC). Protect routes, control navigation, and conditionally show/hide UI elements based on user permissions.
Configuration
Configure the permission system once in your main.js before mounting your app.
The minimum setup is checkPermissions — everything else has sensible defaults.
getCurrentUser option and use setCurrentUser() to write the user
state, hasPermission() updates live inside {#if} blocks
without any subscription wiring. See the next section.Reactive permissions with setCurrentUser() v5.2.0
In v5.0 and v5.1, the canonical example used a custom getCurrentUser that read from
a Svelte 4 store via get(currentUser) — a non-reactive read, so {#if hasPermission(...)} only updated on navigation. A websocket
pushing a permission change wouldn't update the UI until the user clicked a link.
v5.2.0 backs the default currentUserGetter with module-level $state. Every hasPermission() call in a reactive context tracks
user changes automatically.
Wiring up websocket-driven permission updates
$state rune, or any other reactive container), keep passing configurePermissions({ getCurrentUser }) and it'll keep working —
the default is the new behavior, but you can override it. Pass getCurrentUser: null to explicitly reset back to the state-backed default.Re-checking the active route with revalidateCurrentRoute() v5.2.0
Reactive hasPermission() covers UI element visibility — menu
items, buttons, conditional sections. It doesn't cover the case where the user is sitting on a protected page when their permissions are revoked. The router
only checks route conditions during navigation, so a user on /admin who loses
admin permission would stay on /admin until they navigated away.
revalidateCurrentRoute() re-runs guards and conditions against the currently
mounted route without re-mounting the component. On success, nothing visible
happens — the component keeps its state (no flicker, no scroll reset, no in-flight form data
lost). On failure, the same unauthorized handling fires as for fresh navigation, or you can
customize it with onRevalidationFailure.
Safe to call on every websocket message
Calls within a ~50ms window are coalesced into a single re-validation pass. Multiple <Router> instances (nested routers, zones) each register independently
and re-validate their own routes.
Protecting Routes
Use createProtectedRoute() to protect routes with permissions - no wrap() needed!
ANY Permission (OR Logic)
User needs at least ONE of the specified permissions:
ALL Permissions (AND Logic)
User needs ALL of the specified permissions:
Advanced: Using wrap() with createProtectedRouteDefinition()
For more control or when combining with other wrap options, use createProtectedRouteDefinition() with wrap():
Controlling UI Elements
Use hasPermission() to show/hide UI elements based on permissions:
Permission Requirements
ANY (OR Logic)
any: [...]
User needs at least ONE of these permissions.
ALL (AND Logic)
all: [...]
User needs ALL of these permissions.
Practical Examples
Complete Permission Setup
Route Configuration
Navigation with Permissions
Feature Flags with Permissions
Custom Permission Logic
You can implement complex permission checks in your checkPermissions function:
Best Practices
Configure Early
Configure permissions in main.js before mounting your app to ensure the system
is ready when routes are accessed.
Granular Permissions
Use specific permission names like users.read, users.write instead
of broad permissions like admin for better control.
Server-Side Validation
Client-side permissions are for UX only! Always validate permissions on the server for security. Never trust client-side checks alone.
Configure unauthorized behavior
Set unauthorizedBehavior + unauthorizedComponent (or unauthorizedRoute) at config time so failed permission checks have a
consistent UI. The legacy onUnauthorized callback still works for backward
compatibility, but the declarative config is preferred.
Pair reactive permissions with route revalidation
setCurrentUser() updates UI conditionals reactively, but doesn't re-check
the currently mounted route. Call revalidateCurrentRoute() after a
permission update if the user might be sitting on a now-forbidden page.
Integration with Authentication
Combine permissions with your authentication system:
Try It Live
See the permission system in action:
/authorization-demo— asyncauthorizationCallbackfor per-resource checks/admin— permission-protected route (use the Toggle 👤 button in the example header to switch users)/settings— route requiringsettings:managepermission
Source: example/ in the main repo.