React Router Demo
Examples / @xmachines/play-react-router-demo
React Router v7 integration demo for the XMachines Play architecture.
What This Demonstrates
- Shared auth machine reused without framework-specific business logic
PlayRouterProviderrenderer-based integration with React Router- Shell-driven rendering via
PlayRendererwith actor-authoritative navigation - Canonical TC39 Signals lifecycle mapped to React’s rendering loop
- Non-browser invariant tests plus browser E2E coverage
Running the Demo
From the repository root:
npm installnpm run dev -w packages/play-react-router/examples/demoThen open http://localhost:5173.
Step-by-Step Code Flow
Use this order to understand the implementation:
src/main.tsxmounts<App />.src/App.tsxcreates/starts the actor from shared machine + catalog.- Route metadata is extracted from the machine and converted into a route map.
PlayRouterProviderwirescreateBrowserRouterand actor navigation in both directions.Shell(insideApp.tsx) rendersPlayRendererand actor-driven navigation UI.- Browser tests in
test/browser/validate startup and auth navigation flow.
// src/main.tsx (shape)createRoot(document.getElementById("root")!).render( <StrictMode> <App /> </StrictMode>,);// src/App.tsx (shape)const createPlayer = definePlayer({ machine: authMachine, catalog });const actor = createPlayer();actor.start();
const routeTree = extractMachineRoutes(authMachine);const routeMap = createRouteMapFromTree(routeTree);
return ( <PlayRouterProvider actor={actor} router={router} routeMap={routeMap} renderer={(currentActor, currentRouter) => { void currentActor; return <RouterProvider router={currentRouter} />; }} />);// src/components/Login.tsx (shape)export function Login() { return ( <button onClick={() => actor.send({ type: "auth.login", username, password })}> Login </button> );}Key Files
src/main.tsx- React entry point that mounts<App />src/App.tsx- actor lifecycle, route extraction, provider wiring, and renderer compositionsrc/components/- demo views bound to catalog component keys (Home, Login, Dashboard, Profile, etc.)test/browser/startup.browser.test.tsx- startup assertion for public home + login controltest/browser/auth-flow.browser.test.tsx- end-to-end login -> dashboard -> profile -> logout flow
State Machine & Architecture Details
The demo utilizes XMachines architectural invariants:
- Actor Authority: Navigation triggers URL changes, which the
PlayRouterProviderintercepts and converts intoplay.routeevents. The actor evaluates these events against its internal guards and transitions. - Passive Infrastructure: The router does not execute business logic. The actor dictates whether navigation is permitted. The React application only triggers renders.
- Signal-Only Reactivity: The bridge leverages React’s
useSyncExternalStore(internally within the hooks) to react precisely when signals update, without polluting the React component tree withuseStateoruseEffectfor business logic.
Watcher Lifecycle and Cleanup Contract
This demo follows the same watcher lifecycle used across @xmachines/play-signals and framework adapters:
notifyqueueMicrotask- Drain pending work with
getPending() - Read actor signals and project framework-local state
- Re-arm via
watch()/watch(...signals)
Watcher notifications are one-shot. Cleanup is explicit: bridge/provider teardown must call disconnect/unwatch paths, never rely on GC-only cleanup. The PlayRouterProvider handles this natively on component unmount.
Adapter Boundaries
PlayRouterProvider and the React Router bridge stay passive infrastructure. Business validity remains actor-owned, while RouterBridgeBase remains the shared policy layer and the concrete React adapter stays a thin port, delegating DOM synchronization to React Router.
Available Scripts
These commands are defined in package.json:
| Command | Description |
|---|---|
npm run dev -w packages/play-react-router/examples/demo | Start Vite dev server |
npm run build -w packages/play-react-router/examples/demo | Build production bundle |
npm run preview -w packages/play-react-router/examples/demo | Preview built bundle |
npm run test -w packages/play-react-router/examples/demo | Run Vitest test suite |
npm run test:browser -w packages/play-react-router/examples/demo | Run browser-focused Vitest suite |
Verification
Use these checks to validate README claims against the current demo implementation:
npm run test -w packages/play-react-router/examples/demonpm run test:browser -w packages/play-react-router/examples/demoExpected result: tests pass for startup and auth-flow browser scenarios.