@keenmate/svelte-spa-router
v5.3.0
Published
Router for SPAs using Svelte 5 with runes, dual-mode routing, permissions, and error handling
Downloads
15,504
Readme
@keenmate/svelte-spa-router
A modern router for Svelte 5 SPAs, built from the ground up on the runes API ($props, $state, $effect, $derived). Supports dual-mode routing (hash and history) with comprehensive permission management.
MIT licensed.
Main features
- Dual-mode routing — hash (
#/path) or history API (/path) - Built on Svelte 5 runes — no stores, no
$:declarations - Type-safe routes —
defineRoutes()gives IDE autocomplete on route names and params - TypeScript-first — generics on
routeParams(),query(),filters() - Flexible navigation — multi-parameter signatures, named routes, navigation context, referrer tracking
- Hierarchical routes — optional parent→child inheritance of breadcrumbs, permissions, and guards
- Permissions — role-based AND resource-based access control, with live reactivity via
setCurrentUser() - Querystring & filter helpers — reactive, type-safe, with auto-detection of array formats
- Global error handler — production-ready, with restart-loop prevention
- 404 tracking —
onNotFoundcallback for analytics/monitoring - Tiny — uses regexparam for route matching, distributed as source (no build step)
What's new
v5.3.0
- BREAKING:
NavTreeNode.isHiddenrenamed tohidden— aligns with the KeenMate web-components convention of bare HTML attribute names (hidden,disabled,selected) for data-model boolean fields. Same shape, same getter reactivity. Migration: find-and-replaceisHidden:→hidden:in your nav-tree definitions. The helper predicateisNodeHidden(node)keeps itsis*prefix. helpers/nav-tree— permission-aware filtering for auto-generated sidebars —filterByPermissions(navTree, { mode })drops or marks-as-disabled nodes the current user can't reach. Two modes (hide/disable), cascading parent hiding, ancestor permission inheritance, and a polymorphichidden: boolean | () => booleanper node for static or reactive overrides. Reactive end-to-end —$derived(filterByPermissions(navTree))re-runs whensetCurrentUser()fires or anyhiddengetter reads mutating$state. No subscription wiring. Ships withwalkTree/findNodeByPathand a/nav-tree-demoreference implementation where one tree drives both routes AND sidebar.subtree: true+subtreeClassNameonuse:active— sidebar parents stay highlighted on their own index page AND every nested URL from a single action call, no regex required.subtreeClassNameadds a distinct class on "parent of an active child" vs "really active" links (e.g.link-active/sublink-active) in one call instead of two stacked actions.NavTreeNode.disablednow renders as forbidden in BOTH filter modes —disabledis a product-level placeholder signal ("coming soon"), not a user-permission concern, so it stays visible regardless of hide/disable mode. NewFilterOptions.disabledClassNamestyles these distinctly from permission-denied items (e.g. amber "Unavailable" vs. red "Access restricted"); when both apply,disabledClassNamewins. Default falls back toforbiddenClassName, fully backward compatible.- Stacked
use:activeactions on the same node now cooperate — class management rewritten from per-entry remove-then-conditionally-add to a per-node aggregate sync. Fixes a silent bug where the common "parent + descendants" pattern (use:active use:active={'/foo/*'}) failed on the bare path. ai/link-actions.txtrewritten — corrected the PREFIX MATCHING claim that/foo/*matched bare/foo(it doesn't), with new BRANCH MATCHING, SIDEBAR WITH SUBMENU, TWO-CLASS PARENT/CHILD, GENERATING NAV FROM ROUTE TREE, and FILTERING BY PERMISSIONS sections.
Full details in CHANGELOG.md.
Installation
npm install @keenmate/svelte-spa-router⚠️ Important: This package requires Node.js 22 or higher for production builds. Node.js 20 has compatibility issues with Svelte 5 that may cause runtime errors like "link is not defined" in production builds. Make sure your build environment (CI/CD, Docker, etc.) uses Node 22+.
Quick start
1. Define your routes:
// routes.js
import Home from './routes/Home.svelte'
import About from './routes/About.svelte'
import User from './routes/User.svelte'
import NotFound from './routes/NotFound.svelte'
export default {
'/': Home,
'/about': About,
'/user/:id': User,
'*': NotFound, // Catch-all (must be last)
}2. Mount the router:
<!-- App.svelte -->
<script>
import Router from '@keenmate/svelte-spa-router'
import routes from './routes'
</script>
<Router {routes} />3. Add links and navigate:
<script>
import { link, push } from '@keenmate/svelte-spa-router'
</script>
<nav>
<a href="/" use:link>Home</a>
<a href="/about" use:link>About</a>
<a href="/user/42" use:link>User 42</a>
</nav>
<button onclick={() => push('/user/99')}>Go to User 99</button>4. Read route params inside a route component:
<!-- User.svelte -->
<script>
let { routeParams = {} } = $props()
</script>
<p>User ID: {routeParams.id}</p>That's it. By default the router uses hash mode (#/path) so no server config is needed. For clean URLs, switch to history mode — see Routing modes.
For type-safe routes with autocomplete on names and params, see defineRoutes().
Documentation
- Imports & exports — which path to import from, plus the
/storesgotcha - Routing modes — hash vs history, server config, base path
- Defining routes — basic routes,
defineRoutes(),createRoute(),wrap(), code-splitting - Navigation —
push/replace/pop/goBack, named routes, navigation context, referrer tracking - Route params, location & querystring — accessing route data, TypeScript generics
- Querystring & filter helpers — reactive URL-driven UIs, flat & OData-style filters
- Loading states & metadata — three loading patterns, dynamic title & breadcrumbs
- Route guards (pre-conditions) —
wrap({ conditions })and the failure flow - Permissions —
createProtectedRoute(),hasPermission(), role + resource auth, conditions vs permissions - Navigation guards (beforeLeave) — unsaved-changes confirmations, dirty checks, browser back/close
- Hierarchical routes — tree structure with
createHierarchy(), flat-mode inheritance - Error handling & 404 tracking — global error handler, recovery strategies,
onNotFound - Debug logging — 12 categorized loggers, runtime control from the browser console
- Advanced topics — pipeline architecture, scroll restoration, nested routers, event payloads, full import reference
Other guides in the repo root:
- CHANGELOG.md — full release history
- MIGRATION.md — migration guide from other routers
- DEVELOPMENT.md — development workflow
Examples
This repository includes three complete example applications:
example/— hash mode routing with basic featuresexample-history/— history mode with clean URLs, querystring demos, filter demosexample-permissions/— permission-based routing with role management
Run an example:
cd example-history
npm install
npm run devLicense
MIT — see LICENSE.md for details.
