@xmachines/play-sveltekit-router
API / @xmachines/play-sveltekit-router
SvelteKit router adapter for the XMachines Universal Player Architecture. It keeps the state machine routes of a RoutableActor and the browser URL in step, in both directions, through the $app/navigation module of SvelteKit.
Installation
pnpm add @xmachines/play-sveltekit-routerPeer dependencies. Install them separately:
pnpm add @sveltejs/kit svelte xstate| Peer dependency | Required version |
|---|---|
@sveltejs/kit | ^2.0.0 |
svelte | ^5.0.0 |
xstate | ^5.31.0 |
Quick Start
import { onDestroy } from "svelte";import { connectRouter, createRouteMap } from "@xmachines/play-sveltekit-router";import { definePlayer } from "@xmachines/play-xstate";import { myMachine } from "./machine.js";
// 1. Create the actorconst createPlayer = definePlayer({ machine: myMachine });const actor = createPlayer();actor.start();
// 2. Build a bidirectional route map from the machine definitionconst routeMap = createRouteMap(myMachine);
// 3. Connect the router — returns a cleanup functionconst disconnect = connectRouter({ actor, routeMap });
// 4. Clean up when the component is destroyedonDestroy(() => disconnect());The bridge works in both directions:
- URL → actor: the bridge converts each SvelteKit
afterNavigateevent into aplay.routeevent, then sends it to the actor. - Actor → URL: the bridge writes each change of the actor
currentRoutesignal to the browser URL withgoto().
Usage
connectRouter(options) — high-level API
This function is the primary integration point. It creates a SvelteKitRouterBridge, calls connect(), and returns a cleanup function.
import { onDestroy } from "svelte";import { connectRouter, createRouteMap } from "@xmachines/play-sveltekit-router";
// machine and actor as in the Quick Start aboveconst routeMap = createRouteMap(machine);
// In a Svelte component or SvelteKit layoutconst disconnect = connectRouter({ actor, routeMap });
// Or pass an explicit location for SSR or test environmentsconst disconnectSsr = connectRouter({ actor, routeMap, location: { pathname: "/dashboard", search: "?tab=stats" },});
// CleanuponDestroy(() => disconnect());ConnectRouterOptions:
| Property | Type | Required | Description |
|---|---|---|---|
actor | RoutableActor | Yes | The XMachines actor that the bridge keeps in step with the router |
routeMap | RouteMap | Yes | The bidirectional map between the state IDs and the URL paths |
location | LocationLike | null | No | The location stub for the initial URL reads. The default is globalThis.location. Give a mock in a test, or a stub in an SSR environment. |
SvelteKitRouterBridge — low-level class
This class extends RouterBridgeBase from @xmachines/play-router. Use it directly when you need more control over the connection lifecycle.
import { SvelteKitRouterBridge, createRouteMap } from "@xmachines/play-sveltekit-router";
// machine and actor as in the Quick Start aboveconst routeMap = createRouteMap(machine);const bridge = new SvelteKitRouterBridge(actor, routeMap, location);bridge.connect();
// On teardownbridge.disconnect();SvelteKit integration details:
navigateRouter→goto(path, { noScroll: true, keepFocus: true })watchRouterChanges→ registers anafterNavigatecallback through an indirection cell, which prevents a direct capture ofthisunwatchRouterChanges→ sets the indirection cell to null, which releases the bridge reference before the component unmounts (SvelteKit has no API to cancelafterNavigate)
createRouteMap(machine)
This factory builds a RouteMap from an XState machine definition. It reads each route pattern from state.meta.route.
import { createRouteMap } from "@xmachines/play-sveltekit-router";import { createMachine } from "xstate";
const machine = createMachine({ id: "app", initial: "home", states: { home: { meta: { route: "/home" } }, dashboard: { meta: { route: "/dashboard" } }, settings: { meta: { route: "/settings/:section?" } }, },});
const routeMap = createRouteMap(machine);RouteMap / RouteMapping
The bidirectional map between the state IDs and the URL paths. It comes from @xmachines/play-router.
import { RouteMap } from "@xmachines/play-sveltekit-router";
const routeMap = new RouteMap([ { stateId: "#app.home", path: "/home" }, { stateId: "#app.dashboard", path: "/dashboard" }, { stateId: "#app.settings", path: "/settings/:section?" },]);API Summary
Exported symbols
| Export | Kind | Description |
|---|---|---|
connectRouter | function | High-level factory — connects the SvelteKit router to an actor, returns a cleanup function |
SvelteKitRouterBridge | class | The low-level bridge class. It extends RouterBridgeBase. Use it directly for full lifecycle control |
ConnectRouterOptions | type | The options object for connectRouter |
createRouteMap | function | Builds a RouteMap from an XState machine definition |
RouteMap | class | Bidirectional state ID ↔ URL path mapping (re-exported from @xmachines/play-router) |
RouteMapping | type | Single { stateId, path } entry for RouteMap construction |
RouteMapOptions | type | Options for RouteMap construction |
LocationLike | type | Minimal { pathname, search } interface for location stubs |
PlayRouteEvent | type | The event that the bridge sends to the actor on a URL change ({ type: "play.route", to, params?, query? }) |
RouterBridge | type | The interface that SvelteKitRouterBridge implements |
RoutableActor | type | Minimal actor interface from @xmachines/play-router — currentRoute, initialRoute, send(PlayRouteEvent) |
Demo
examples/demo/ holds a runnable Svelte 5 and SvelteKit demo of the adapter. Run it from the monorepo root:
pnpm installpnpm --filter @xmachines/play-sveltekit-router-demo run devThen open http://localhost:5173.
Testing
Run tests for this package in isolation:
# From the monorepo rootpnpm --filter @xmachines/play-sveltekit-router test
# Or from this package directorypnpm testCoverage thresholds: 80% lines, functions, branches, and statements.
License
MIT — see LICENSE.