@xmachines/play-svelte-spa-router
API / @xmachines/play-svelte-spa-router
Svelte SPA Router adapter for the XMachines Play architecture. It connects hash-based routing to a
Routableactor, so the state machine owns the navigation.
Installation
pnpm add @xmachines/play-svelte-spa-router @xmachines/play-router @xmachines/core @xmachines/play @xmachines/play-atom@xmachines/play-router names @xmachines/core, @xmachines/play and @xmachines/play-atom as peers, so the command installs them with this package.
Peer dependencies. Install them with the adapter:
pnpm add svelte@^5.0.0 svelte-spa-router@^5.0.0Usage
1. Define a routable XState machine
Declare the route of each state in its meta.route field:
import { setup } from "xstate";import { formatPlayRouteTransitions } from "@xmachines/play-xstate/routing";import type { PlayRouteEvent } from "@xmachines/play-svelte-spa-router";
const machine = setup({ types: { events: {} as PlayRouteEvent },}).createMachine( formatPlayRouteTransitions({ id: "app", initial: "home", states: { home: { id: "home", meta: { route: "/home" } }, dashboard: { id: "dashboard", meta: { route: "/dashboard" } }, settings: { id: "settings", meta: { route: "/settings/:section?" } }, }, }),);2. Create a player and connect the router
Use createRouteMap to build a bidirectional route map from the machine. Then call connectRouter one time, usually in your root Svelte component or in a dedicated runtime module:
import { definePlayer, compose, PlayerActor } from "@xmachines/play-xstate";import { withRouting } from "@xmachines/play-xstate/routing";import { connectRouter } from "@xmachines/play-svelte-spa-router";import { createRouteMap } from "@xmachines/play-router/xstate";import type { AnyStateMachine } from "xstate";
declare const machine: AnyStateMachine; // the machine of the previous example
// `connectRouter` needs the routing capabilityconst createPlayer = definePlayer({ machine, actor: compose(PlayerActor, withRouting) });export const actor = createPlayer();actor.start();
const routeMap = createRouteMap(machine);export const disconnectRouter = connectRouter({ actor, routeMap });3. Clean up on destroy
Give the cleanup function of connectRouter to onDestroy, or to the equivalent lifecycle hook:
<script lang="ts"> import { onDestroy } from "svelte"; import { disconnectRouter } from "./runtime.js"; // the module from the previous example
onDestroy(() => disconnectRouter());</script>API Summary
connectRouter(options): () => void
Connects svelte-spa-router to a Routable actor. Returns a cleanup (disconnect) function.
import type { RoutableActor, RouteMap, WindowLike } from "@xmachines/play-svelte-spa-router";
interface ConnectRouterOptions { readonly actor: RoutableActor; readonly routeMap: RouteMap; /** * Window-like object for `hashchange` subscriptions. * Defaults to the global `window`. Pass a mock in tests * or a no-op in SSR environments. */ readonly window?: WindowLike;}Behavior:
- On
connect: it reads the current hash URL fromrouter.loc.location, sets the actor state with aplay.routeevent, and then listens for ahashchangeevent. - On a change of the actor
currentRoute: it callspush(path)from svelte-spa-router to update the URL. - On
hashchange: it reads the new location fromrouter.locand sendsplay.routeto the actor. - On disconnect: it removes the
hashchangelistener and cancels the subscriptions to the actor atoms.
createRouteMap(machine): RouteMap — from @xmachines/play-router/xstate
This package re-exports it never: the barrel names no state machine. Import the function from @xmachines/play-router/xstate. It reads every meta.route entry of an XState machine. Then it builds a bidirectional route map with URLPattern matching, which includes a parameter segment and an optional segment.
RouteMap
This type comes from @xmachines/play-router. It is the bidirectional map between the state IDs and the URL paths.
| Method | Description |
|---|---|
getStateIdByPath(path) | Returns the state ID for a URL path, or null |
getPathByStateId(stateId) | Returns the URL path for a state ID, or null |
Exported types
| Type | Description |
|---|---|
ConnectRouterOptions | The options that connectRouter accepts |
RoutableActor | Minimal actor interface — PlayActor from @xmachines/play-actor combined with the Routable mixin |
PlayRouteEvent | The play.route event type that the bridge sends to the actor |
RouterBridge | Interface that all router bridges must satisfy |
RouteMapping | Route-entry shape within a RouteMap |
RouteMapOptions | Options for the RouteMap construction |
WindowLike | Minimal window interface for testability |
How it works
SvelteSpaRouterBridge extends RouterBridgeBase from @xmachines/play-router and implements three abstract methods:
| Method | svelte-spa-router equivalent |
|---|---|
navigateRouter(path) | push(path) |
watchRouterChanges() | win.addEventListener("hashchange", …) |
unwatchRouterChanges() | win.removeEventListener("hashchange", …) |
The bridge reads the initial path from router.loc.location, and the initial search string from router.loc.querystring. It inherits the restore-or-deeplink detection, the guard-redirect flow, and the isProcessingNavigation debounce from RouterBridgeBase.
SvelteSpaRouterBridge is exported from the package — prefer connectRouter for the common case, and instantiate the bridge directly (new SvelteSpaRouterBridge(actor, routeMap) followed by connect()/disconnect()) when you need fine-grained control over the connection lifecycle.
License
MIT © XMachines Contributors. See LICENSE.
Classes
Interfaces
- BasePathOptions
- ConnectRouterOptions
- PlayRouteEvent
- RoutableActor
- RouteMapOptions
- RouteMapping
- RouterBridge
- RouterConnection
- WindowLike