@xmachines/play-react
API / @xmachines/play-react
React renderer for XMachines Play architecture with signal-driven rendering.
Part of the xmachines-js monorepo.
Installation
npm install @xmachines/play-reactPeer dependencies (must be installed separately):
npm install react react-dom xstate @xstate/store @json-render/react @json-render/core @json-render/xstateSupported versions:
react/react-dom:^18.0.0 || ^19.0.0xstate:^5.31.0@xstate/store:^3.17.0@json-render/*:^0.18.0
Usage
Standard usage — PlayUIProvider + PlayRenderer
The recommended pattern for actor-driven React rendering:
import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-react";import { definePlayer } from "@xmachines/play-xstate";
// 1. Create and start the actorconst actor = definePlayer({ machine: myMachine })();actor.start();
// 2. Define the component registry with action handlersconst registryResult = defineRegistry(myCatalog, { components: { Login, Dashboard }, actions: { login: async ({ username }) => actor.send({ type: "auth.login", username }), logout: async () => actor.send({ type: "auth.logout" }), },});
// 3. Render — signals drive view transitions automaticallyfunction App() { return ( <PlayUIProvider actor={actor} registryResult={registryResult}> <PlayRenderer /> </PlayUIProvider> );}With optional JSONUIProvider props
Pass navigation and validation helpers through PlayUIProvider:
<PlayUIProvider actor={actor} registryResult={registryResult} navigate={(path) => router.push(path)} validationFunctions={{ isEmail: (v) => /^.+@.+$/.test(String(v)) }}> <PlayRenderer /></PlayUIProvider>Escape hatch — custom provider composition
Use ActorProvider directly when you need to compose providers manually:
import { ActorProvider, JSONUIProvider, PlayRenderer } from "@xmachines/play-react";
<ActorProvider actor={actor} registryResult={registryResult}> <JSONUIProvider registry={registryResult.registry}> <PlayRenderer /> </JSONUIProvider></ActorProvider>;Accessing the actor from inside the tree
import { useActor } from "@xmachines/play-react";
function SubmitButton() { const actor = useActor(); return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;}Subscribing to signals directly
import { useSignalEffect } from "@xmachines/play-react";
function MyComponent({ actor }) { const [view, setView] = useState(null);
useSignalEffect(() => { setView(actor.currentView.get()); });
return <div>{view?.component}</div>;}API Summary
Components
| Export | Description |
|---|---|
<PlayUIProvider> | Batteries-included provider: wraps ActorProvider + JSONUIProvider. Standard entry point. |
<PlayRenderer> | Zero-prop leaf component. Reads the current actor view from context and renders it. Must be inside PlayUIProvider or ActorProvider. |
<ActorProvider> | Escape-hatch primitive. Owns actor bridging, signal subscription, and per-view StateStore lifecycle. |
<PlayErrorBoundary> | React class error boundary for catching catalog component render errors. |
Hooks
| Export | Description |
|---|---|
useSignalEffect(callback) | Subscribes to TC39 signal changes; re-runs the callback and forces a React re-render when any accessed signal changes. Cleanup is automatic on unmount. |
useActor() | Returns the raw actor instance. Must be called inside an ActorProvider/PlayUIProvider tree. |
usePlayView() | Returns { spec, handlers, registry, store } for the current view. Must be called inside an ActorProvider/PlayUIProvider tree. |
Types
| Export | Description |
|---|---|
PlayUIProviderProps | Props for <PlayUIProvider> |
ActorProviderProps | Props for <ActorProvider> (also exported as PlayRendererProps for migration compatibility) |
PlayErrorBoundaryProps | Props for <PlayErrorBoundary> |
PlayErrorBoundaryState | State shape for <PlayErrorBoundary> |
AnyPlayActor | Type alias for AbstractActor<AnyActorLogic> — the bare actor type used by context providers |
ViewContextValue | Value shape returned by usePlayView() |
RenderErrorHandler | Error handler callback type for render errors |
Re-exports from @json-render/react
@xmachines/play-react re-exports the full @json-render/react surface so consumers only need one import:
import { defineRegistry, useBoundProp, JSONUIProvider, StateProvider, ActionProvider, VisibilityProvider, ValidationProvider, Renderer,} from "@xmachines/play-react";Key Principle
React state is never used for business logic — only for triggering React’s render cycle. Signals (@xmachines/play-signals) are the source of truth. PlayUIProvider passively observes actor signals via useSignalEffect and re-renders when the current view changes. Rapid signal updates are batched via microtasks to prevent unnecessary React renders.
Testing
Run unit tests (jsdom environment):
npm test -w @xmachines/play-reactRun tests with coverage:
npm run test:coverage -w @xmachines/play-reactRun browser integration tests (requires Chromium):
npm run test:browser -w @xmachines/play-reactCoverage thresholds: 80% lines, functions, branches, and statements.
License
MIT — see LICENSE.
@xmachines/play-react - React renderer for XMachines Play architecture
Provides a provider-based React rendering layer that passively observes actor signals and renders UI components via @json-render/react. This package enables framework-swappable architecture where React is just a rendering target that subscribes to signal changes.
Key principle: React state is NEVER used for business logic—only for triggering React’s render cycle. Signals are the source of truth.
Standard usage:
<PlayUIProvider actor={actor} registryResult={registryResult}> <PlayRenderer /></PlayUIProvider>Escape hatch (custom composition):
<ActorProvider actor={actor} registryResult={registryResult}> <JSONUIProvider registry={registryResult.registry}> <PlayRenderer /> </JSONUIProvider></ActorProvider>Classes
Interfaces
- ActionProviderProps
- ActorProviderProps
- ComponentContext
- JSONUIProviderProps
- PlayErrorBoundaryProps
- PlayErrorBoundaryState
- PlayUIProviderProps
- RendererProps
- StateProviderProps
- ValidationProviderProps
- ViewContextValue
- VisibilityProviderProps
Type Aliases
Variables
Functions
- ActionProvider
- defineRegistry
- JSONUIProvider
- Renderer
- StateProvider
- useActor
- useBoundProp
- usePlayView
- useSignalEffect
- ValidationProvider
- VisibilityProvider
References
PlayRendererProps
Renames and re-exports ActorProviderProps
RenderErrorHandler
Re-exports RenderErrorHandler