npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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

npm GitHub

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 routesdefineRoutes() 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 trackingonNotFound callback for analytics/monitoring
  • Tiny — uses regexparam for route matching, distributed as source (no build step)

What's new

v5.3.0

  • BREAKING: NavTreeNode.isHidden renamed to hidden — 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-replace isHidden:hidden: in your nav-tree definitions. The helper predicate isNodeHidden(node) keeps its is* prefix.
  • helpers/nav-tree — permission-aware filtering for auto-generated sidebarsfilterByPermissions(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 polymorphic hidden: boolean | () => boolean per node for static or reactive overrides. Reactive end-to-end — $derived(filterByPermissions(navTree)) re-runs when setCurrentUser() fires or any hidden getter reads mutating $state. No subscription wiring. Ships with walkTree / findNodeByPath and a /nav-tree-demo reference implementation where one tree drives both routes AND sidebar.
  • subtree: true + subtreeClassName on use:active — sidebar parents stay highlighted on their own index page AND every nested URL from a single action call, no regex required. subtreeClassName adds 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.disabled now renders as forbidden in BOTH filter modesdisabled is a product-level placeholder signal ("coming soon"), not a user-permission concern, so it stays visible regardless of hide/disable mode. New FilterOptions.disabledClassName styles these distinctly from permission-denied items (e.g. amber "Unavailable" vs. red "Access restricted"); when both apply, disabledClassName wins. Default falls back to forbiddenClassName, fully backward compatible.
  • Stacked use:active actions 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.txt rewritten — 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

Other guides in the repo root:

Examples

This repository includes three complete example applications:

  • example/ — hash mode routing with basic features
  • example-history/ — history mode with clean URLs, querystring demos, filter demos
  • example-permissions/ — permission-based routing with role management

Run an example:

cd example-history
npm install
npm run dev

License

MIT — see LICENSE.md for details.