@xmachines/play-solid-router
API / @xmachines/play-solid-router
SolidJS Router adapter for the XMachines Universal Player Architecture. Provides bidirectional synchronisation between a PlayerActor’s state machine routes and the browser URL via @solidjs/router.
Part of the xmachines-js monorepo.
Installation
npm install @xmachines/play-solid-routerPeer dependencies (must be installed separately):
npm install solid-js @solidjs/router xstatesolid-js^1.8.0@solidjs/router^0.16.1xstate^5.31.0
Quick Start
import { Router, Route, useNavigate, useLocation, useParams } from "@solidjs/router";import { onCleanup, type ParentComponent } from "solid-js";import { PlayRouterProvider, createRouteMap } from "@xmachines/play-solid-router";import { definePlayer } from "@xmachines/play-xstate";import { myMachine } from "./machine.js";
const actor = definePlayer({ machine: myMachine })();actor.start();
const routeMap = createRouteMap(myMachine);
const Layout: ParentComponent = () => { const navigate = useNavigate(); const location = useLocation(); const params = useParams();
onCleanup(() => actor.stop());
return ( <PlayRouterProvider actor={actor} routeMap={routeMap} router={{ navigate, location, params }} renderer={(a, router) => <MyApp actor={a} />} /> );};
export default function App() { return <Router root={Layout}>{/* one <Route> per routable state */}</Router>;}API Summary
PlayRouterProvider
A SolidJS component that wires a PlayerActor to Solid Router. It creates and connects a SolidRouterBridge on mount and disconnects it via onCleanup on unmount.
interface PlayRouterProviderProps<TActor extends PlayActor> { /** The actor to sync with Solid Router. */ actor: TActor; /** Bidirectional route map for state ID ↔ URL path lookups. */ routeMap: RouteMap; /** * The three Solid Router hook results that drive bidirectional sync. * Must be obtained via useNavigate(), useLocation(), and useParams() * inside a router context. */ router: SolidRouterHooks; /** Render callback — receives the concrete actor type and router hooks. */ renderer: (actor: TActor, router: SolidRouterHooks) => JSX.Element;}SolidRouterBridge
Low-level class for manual integration. Extends RouterBridgeBase from @xmachines/play-router and uses Solid’s createEffect for reactive router→actor sync.
Important:
connect()must be called inside a Solid reactive owner (component orcreateRoot). Cleanup is not automatic — calldisconnect()(ordispose()) explicitly, typically inonCleanup().
import { useNavigate, useLocation, useParams, onCleanup } from "@solidjs/router";import { SolidRouterBridge, RouteMap } from "@xmachines/play-solid-router";
function App() { const navigate = useNavigate(); const location = useLocation(); const params = useParams();
const routeMap = new RouteMap([ { stateId: "#home", path: "/" }, { stateId: "#profile", path: "/profile/:userId" }, ]);
const bridge = new SolidRouterBridge(navigate, location, params, actor, routeMap); bridge.connect(); onCleanup(() => bridge.disconnect());
return <div>...</div>;}createRouteMap(machine)
Factory that builds a RouteMap directly from an XState machine definition. Re-exported from @xmachines/play-router.
import { createRouteMap } from "@xmachines/play-solid-router";
const routeMap = createRouteMap(myMachine);RouteMap / RouteMapping
Bidirectional state ID ↔ URL path mapping. Re-exported from @xmachines/play-router.
import { RouteMap } from "@xmachines/play-solid-router";
const routeMap = new RouteMap([ { stateId: "#home", path: "/" }, { stateId: "#profile", path: "/profile/:userId" }, { stateId: "#settings", path: "/settings/:section?" },]);Types
| Export | Description |
|---|---|
PlayActor | AbstractActor & Routable & Viewable — canonical actor shape from @xmachines/play-router. Required by PlayRouterProvider, which renders the current view spec in addition to synchronizing routes. |
RoutableActor | Deprecated alias for PlayActor. Use PlayActor from @xmachines/play-router in new code. |
AbstractActor | Re-exported from @xmachines/play-actor for convenience when typing renderer callbacks. |
SolidRouterHooks | Shape of the router prop: { navigate, location, params } |
PlayRouterProviderProps | Full props interface for PlayRouterProvider |
PlayRouteEvent | Event type sent to the actor on URL change (play.route) |
RouterBridge | Interface implemented by SolidRouterBridge |
RouteMapOptions | Options bag for RouteMap construction. Re-exported from @xmachines/play-router. |
Usage Patterns
Protected Routes and Guards
Auth guards live entirely inside the state machine, preventing flashes of unauthorized content:
const machineConfig = { states: { dashboard: { meta: { route: "/dashboard" }, always: { guard: ({ context }) => !context.isAuthenticated, target: "login", }, }, },};When a user navigates to /dashboard while unauthenticated:
- Solid Router updates the URL.
- Bridge intercepts and sends
play.routeto the actor. - Actor evaluates the guard — denies transition, moves to
logininstead. - Bridge observes new actor route (
/login) via TC39 Signal. - Bridge calls
navigate("/login").
Dynamic Routes with Parameters
const routeMap = new RouteMap([ { stateId: "#post", path: "/users/:userId/posts/:postId" }, { stateId: "#settings", path: "/settings/:section?" },]);
// Params are extracted from Solid's useParams() and forwarded in the play.route event:// { type: "play.route", to: "#post", params: { userId: "123", postId: "456" }, query: {} }Path parameters are extracted from Solid’s reactive useParams() proxy — no URLPattern polyfill is needed for parameterized routes.
Testing
Run tests for this package in isolation:
# From the monorepo rootnpm test -w packages/play-solid-router
# Or from this package directorynpm testBrowser tests (test/browser/**/*.browser.test.ts) run against real Chromium via Playwright, covering async sequencing that jsdom cannot faithfully reproduce:
npx vitest --config vitest.browser.config.ts --project play-solid-router-browserCoverage thresholds: 80% lines, functions, branches, and statements.
Related Packages
- @xmachines/play-router — core router primitives and
RouterBridgeBase - @xmachines/play-tanstack-solid-router — TanStack Solid Router adapter
- @xmachines/play-solid — SolidJS view renderer
- @xmachines/play-xstate — XState v5 player factory
Learn More
License
MIT — see LICENSE.
Classes
Interfaces
- AbstractActor
- PlayActor
- PlayRouteEvent
- PlayRouterProviderProps
- RouteMapOptions
- RouteMapping
- RouterBridge