npm.io
1.2.1 • Published 4d ago

@isorouter/svelte

Licence
MIT
Version
1.2.1
Deps
1
Size
16 kB
Vulns
0
Weekly
314

@isorouter/svelte

npm version minzip License: MIT

Svelte 5 bindings for @isorouter/core — a lightweight SPA router built on the browser Navigation API.

<Router>, <Outlet>, <Link> and getRouter() wrap the core router's immutable-snapshot external store with Svelte 5's createSubscriber: reading router.current inside a $derived, $effect or the template subscribes to commits, and the subscription is torn down automatically once nothing reads it anymore — no manual $effect, no leaks.

Full documentation →
Live demo on StackBlitz →

Install

npm install @isorouter/svelte

@isorouter/core is a regular dependency and is installed automatically.

Requirements

  • Svelte ≥ 5.7.
  • Everything in @isorouter/core's Requirements — notably the Navigation API. Load a polyfill if you need to support older engines, before <Router> mounts (it calls router.start() for you).

Quick start

// router.ts
import { createRouter, lazy } from "@isorouter/svelte";

import AppLayout from "./AppLayout.svelte";
import Home from "./Home.svelte";
import About from "./About.svelte";
import DashboardLayout from "./DashboardLayout.svelte";
import Overview from "./Overview.svelte";
import Settings from "./Settings.svelte";

export const router = createRouter([
  {
    path: "/",
    component: AppLayout,
    children: [
      { index: true, title: "Home", component: Home },
      { path: "about", title: "About", component: About },
      {
        path: "dashboard",
        component: DashboardLayout,
        children: [
          { index: true, component: Overview },
          { path: "settings", component: Settings },
        ],
      },
    ],
  },
] as const); // `as const` is required for type-safe navigation

declare module "@isorouter/svelte" {
  interface Register {
    router: typeof router;
  }
}
// main.ts
import { mount } from "svelte";
import App from "./App.svelte";

mount(App, { target: document.getElementById("app")! });
<!-- App.svelte -->
<script lang="ts">
  import { Router } from "@isorouter/svelte";
  import { router } from "./router";
</script>

<Router {router}>
  {#snippet notFound()}
    <p>Not found</p>
  {/snippet}
</Router>
<!-- DashboardLayout.svelte -->
<script lang="ts">
  import { Outlet } from "@isorouter/svelte";
</script>

<h1>Dashboard</h1>
<Outlet />

createRouter returns a SvelteRouter wrapping the core router, with the component type fixed to Svelte's Component. <Router> calls router.start() on mount and router.stop() on unmount.

Components

<Router>
<Router {router}>
  {#snippet loading()}<Spinner />{/snippet}
  {#snippet notFound()}<NotFound />{/snippet}
  {#snippet error(err)}<ErrorPage error={err} />{/snippet}
</Router>
  • router — a SvelteRouter instance from createRouter.
  • notFound — rendered when router.current.status === "not-found".
  • error — called with router.current.error when router.current.status === "error".
  • loading — rendered whenever there's no matched root component yet (e.g. before the first commit) and neither error nor notFound applies.

Snippet priority: error > notFound > matched component > loading.

Otherwise renders the root matched component, router.current.components[0].

<Outlet>

Renders the next component in the matched chain at the current nesting depth. Used inside a layout component to render its matched child route; renders nothing when there is no matching child. The layout instance stays mounted across child navigations.

<Link>
<Link href="/dashboard" activeClass="active" exact>Dashboard</Link>

A plain <a> — the Navigation API intercepts the click, so modifier-clicks, target="_blank" and downloads behave natively. Any other attributes are forwarded to the <a>.

  • href — the target path.
  • class — merged with activeClass.
  • activeClass — appended to class when router.isActive(href, { exact }) (default "active").
  • exact — when set, only an exact URL match is considered active (without it, a parent link stays active on all child routes).

When active, also sets aria-current="page". Must be used within <Router> (or <Outlet>).

getRouter()

<script lang="ts">
  import { getRouter } from "@isorouter/svelte";

  const router = getRouter();
</script>

<p>{router.current.params.city}</p>

Reads the SvelteRouter instance from context. Must be used within <Router> (or <Outlet>). router.current is a getter — read it inside a $derived, $effect or the template to subscribe to commits; the subscription is dropped once nothing reads it. router.navigate, router.back, router.forward and router.isActive are also available on the instance.

Route title

Routes accept an optional title — a string or a function:

{ path: "about", title: "About", component: About }

// Dynamic — receives GuardContext with params, url, signal
{ path: "users/:id", title: (ctx) => `User #${ctx.params.id}`, component: User }

Type-safe navigation

Declare routes as const and augment Register in the same file — then router.navigate only accepts known paths and getRouter() returns the concrete type everywhere:

// router.ts
export const router = createRouter([...] as const);

declare module "@isorouter/svelte" {
  interface Register { router: typeof router; }
}

See @isorouter/core's Type-safe navigation.

License

MIT Mykhailo Pidkhvatylin

Keywords