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.

Configure permissions
 
Reactive by default in v5.2.0. If you skip the 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.

setCurrentUser + reactive hasPermission
 

Wiring up websocket-driven permission updates

 
If you maintain your own reactive user store (your own $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.

Wiring v5.2.0 reactive + revalidation together
 

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:

ANY permission requirement
 

ALL Permissions (AND Logic)

User needs ALL of the specified permissions:

ALL permissions requirement
 

Advanced: Using wrap() with createProtectedRouteDefinition()

For more control or when combining with other wrap options, use createProtectedRouteDefinition() with wrap():

Advanced: Manual wrapping
 

Controlling UI Elements

Use hasPermission() to show/hide UI elements based on permissions:

Conditional UI elements
 

Permission Requirements

ANY (OR Logic)

any: [...]

User needs at least ONE of these permissions.

ANY logic
 
ALL (AND Logic)

all: [...]

User needs ALL of these permissions.

ALL logic
 

Practical Examples

Complete Permission Setup

Complete setup
 

Route Configuration

Route configuration
 

Navigation with Permissions

Conditional navigation
 

Feature Flags with Permissions

Feature flags
 

Custom Permission Logic

You can implement complex permission checks in your checkPermissions function:

Custom permission logic
 

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:

Authentication integration
 

Try It Live

See the permission system in action:

  • /authorization-demo — async authorizationCallback for per-resource checks
  • /admin — permission-protected route (use the Toggle 👤 button in the example header to switch users)
  • /settings — route requiring settings:manage permission

Source: example/ in the main repo.