@xmachines/play-react-router
API / @xmachines/play-react-router
React Router 7 and 8 adapter for the XMachines Play Universal Player Architecture. It keeps the actor state and the browser URL in step, in both directions, through the
createBrowserRouterdata API.
Installation
pnpm add @xmachines/play-react-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 if they are not present:
pnpm add react@"^18 || ^19" react-router@"^7 || ^8"Usage
PlayRouterProvider — Recommended (React component)
PlayRouterProvider connects a PlayerActor to React Router inside a React component tree. It creates a ReactRouterBridge on mount. It keeps the actor state and the browser URL in step, in both directions. It disconnects the bridge on unmount.
All three props (
actor,router,routeMap) must be stable references. Create them outside the JSX, or hold them withuseMemo. An inline value makes the bridge disconnect and connect again on every render.
import { useMemo, useEffect } from "react";import { createBrowserRouter, RouterProvider } from "react-router";import { PlayRouterProvider } from "@xmachines/play-react-router";import { createRouteMap } from "@xmachines/play-router/xstate";import { definePlayer, compose, PlayerActor } from "@xmachines/play-xstate";import { withRouting } from "@xmachines/play-xstate/routing";import { myMachine } from "./machine.js";
const createPlayer = definePlayer({ machine: myMachine, actor: compose(PlayerActor, withRouting) });const routeMap = createRouteMap(myMachine);
// Minimal app shell stub — a real app renders PlayUIProvider + PlayRenderer from// @xmachines/play-react here (see the workspace-only @xmachines/play-react-demo Shell)function App({ actor }: { actor: ReturnType<typeof createPlayer> }) { return <main />; // render your UI from the actor here}
function createAppRuntime() { const actor = createPlayer(); actor.start(); const router = createBrowserRouter([{ path: "*", element: <App actor={actor} /> }]); return { actor, router };}
export default function Root() { const { actor, router } = useMemo(createAppRuntime, []);
useEffect(() => () => actor.stop(), [actor]);
return ( <PlayRouterProvider actor={actor} router={router} routeMap={routeMap} renderer={(_, currentRouter) => <RouterProvider router={currentRouter} />} /> );}ReactRouterBridge — Manual (imperative API)
Use ReactRouterBridge directly when you need imperative lifecycle control outside React.
The bridge requires
createBrowserRouter(the data router API). It does not support the old<BrowserRouter>component, because that component has nosubscribeornavigateAPI.
import { createBrowserRouter } from "react-router";import { ReactRouterBridge } from "@xmachines/play-react-router";import { createRouteMap } from "@xmachines/play-router/xstate";import { definePlayer, compose, PlayerActor } from "@xmachines/play-xstate";import { withRouting } from "@xmachines/play-xstate/routing";import { myMachine } from "./machine.js";
// The bridge needs the routing capabilityconst actor = definePlayer({ machine: myMachine, actor: compose(PlayerActor, withRouting) })();actor.start();
const router = createBrowserRouter([/* routes */]);const routeMap = createRouteMap(myMachine);
const bridge = new ReactRouterBridge(router, actor, routeMap);bridge.connect(); // starts bidirectional sync// ... later:bridge.disconnect(); // stops sync and cleans up subscriptionsAPI
PlayRouterProvider
A React component that manages a ReactRouterBridge lifecycle via useEffect.
interface PlayRouterProviderProps<TActor> { /** The actor to sync with React Router. Must be a stable reference. */ actor: TActor; /** The React Router instance returned by `createBrowserRouter`. */ router: BrowserRouterInstance; /** * Bidirectional route map for state ID ↔ URL path lookups. * Must be a stable reference — memoize with useMemo if constructed inline. */ routeMap: RouteMap; /** Render callback — receives the actor and router. */ renderer: (actor: TActor, router: BrowserRouterInstance) => ReactNode;}ReactRouterBridge
Extends RouterBridgeBase from @xmachines/play-router. Implements the RouterBridge protocol.
| Method | Description |
|---|---|
connect() | Subscribes to the router changes and sets the actor state from the current URL |
disconnect() | Cancels the subscription and stops all synchronization |
Types exported from this package
| Export | Description |
|---|---|
PlayRouterProviderProps | Props interface for PlayRouterProvider |
RoutableActor | The constraint type for an actor that PlayRouterProvider and the bridge accept |
Route map utilities (re-exported from @xmachines/play-router)
This barrel names no state machine. createRouteMap and extractMachineRoutes read one,
so import them from @xmachines/play-router/xstate.
| Export | Description |
|---|---|
RouteMap | Bidirectional state ID ↔ URL path map |
createRouteMapFromTree(tree) | Build a RouteMap from a RouteTree object |
RouteMapOptions | Options type for the RouteMap construction |
RouteMapping | Type for a single { stateId, path } entry |
RouterBridge | Interface that ReactRouterBridge satisfies |
PlayRouteEvent | The play.route event type that a router sends to an actor on navigation |
License
@xmachines/play-react-router
React Router 7/8 adapter for the XMachines Play architecture. It keeps the browser URL and the actor state in step through the createBrowserRouter data API.