Skip to content

@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 createBrowserRouter data API.

License: MIT Version


Installation

Terminal window
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:

Terminal window
pnpm add react@"^18 || ^19" react-router@"^7 || ^8"

Usage

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 with useMemo. 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 no subscribe or navigate API.

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 capability
const 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 subscriptions

API

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.

MethodDescription
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

ExportDescription
PlayRouterProviderPropsProps interface for PlayRouterProvider
RoutableActorThe 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.

ExportDescription
RouteMapBidirectional state ID ↔ URL path map
createRouteMapFromTree(tree)Build a RouteMap from a RouteTree object
RouteMapOptionsOptions type for the RouteMap construction
RouteMappingType for a single { stateId, path } entry
RouterBridgeInterface that ReactRouterBridge satisfies
PlayRouteEventThe play.route event type that a router sends to an actor on navigation

License

MIT

@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.

Classes

Interfaces

Type Aliases

Variables

Functions