Skip to content

@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

Terminal window
npm install @xmachines/play-solid-router

Peer dependencies (must be installed separately):

Terminal window
npm install solid-js @solidjs/router xstate
  • solid-js ^1.8.0
  • @solidjs/router ^0.16.1
  • xstate ^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 or createRoot). Cleanup is not automatic — call disconnect() (or dispose()) explicitly, typically in onCleanup().

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

ExportDescription
PlayActorAbstractActor & Routable & Viewable — canonical actor shape from @xmachines/play-router. Required by PlayRouterProvider, which renders the current view spec in addition to synchronizing routes.
RoutableActorDeprecated alias for PlayActor. Use PlayActor from @xmachines/play-router in new code.
AbstractActorRe-exported from @xmachines/play-actor for convenience when typing renderer callbacks.
SolidRouterHooksShape of the router prop: { navigate, location, params }
PlayRouterProviderPropsFull props interface for PlayRouterProvider
PlayRouteEventEvent type sent to the actor on URL change (play.route)
RouterBridgeInterface implemented by SolidRouterBridge
RouteMapOptionsOptions 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:

  1. Solid Router updates the URL.
  2. Bridge intercepts and sends play.route to the actor.
  3. Actor evaluates the guard — denies transition, moves to login instead.
  4. Bridge observes new actor route (/login) via TC39 Signal.
  5. 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:

Terminal window
# From the monorepo root
npm test -w packages/play-solid-router
# Or from this package directory
npm test

Browser tests (test/browser/**/*.browser.test.ts) run against real Chromium via Playwright, covering async sequencing that jsdom cannot faithfully reproduce:

Terminal window
npx vitest --config vitest.browser.config.ts --project play-solid-router-browser

Coverage thresholds: 80% lines, functions, branches, and statements.

Learn More

License

MIT — see LICENSE.

Classes

Interfaces

Type Aliases

Functions