Skip to content

@xmachines/play-solid

API / @xmachines/play-solid

Solid renderer for XMachines Play architecture

License: MIT Version

The SolidJS rendering layer observes the actor atoms and renders the UI components through @xmachines/json-render-solid. SolidJS reactivity only triggers the re-render. The atoms are the source of truth.

Installation

Terminal window
pnpm add @xmachines/play-solid

Peer dependencies. Install them with the package:

Terminal window
pnpm add solid-js @xmachines/core @xmachines/play @xmachines/play-atom @xstate/store @xmachines/json-render-solid @xmachines/json-render-core @xmachines/json-render-xstate

@xstate/store is the peer of @xmachines/play-atom, and not of this package: this package reads the atom through @xmachines/play-atom and imports the engine in no source file. Install ONE copy of it.

Quick Start

import { PlayUIProvider, PlayRenderer, defineRegistry, schema } from "@xmachines/play-solid";
import { definePlayer, compose, PlayerActor } from "@xmachines/play-xstate";
import { withView } from "@xmachines/play-xstate/view";
import { defineCatalog } from "@xmachines/json-render-core";
import { myMachine } from "./machine.js"; // your xstate machine (states carry meta.view specs)
// authCatalogDef is a plain object describing components/actions — in this repo it
// comes from the workspace-only @xmachines/play-actor-shared demo package
import { authCatalogDef } from "@xmachines/play-actor-shared";
// 1. Define a catalog
const catalog = defineCatalog(schema, authCatalogDef);
// 2. Create and start an actor — a renderer needs the view capability
const createPlayer = definePlayer({ machine: myMachine, actor: compose(PlayerActor, withView) });
const actor = createPlayer();
actor.start();
// 3. Build a component registry
const registryResult = defineRegistry(catalog, {
components: {
Home: () => <div>Welcome home!</div>,
Login: (ctx) => <div>Login {ctx.props.username && <span>{ctx.props.username}</span>}</div>,
},
actions: {
login: async (args) => actor.send({ type: "auth.login", username: args.username }),
logout: async () => actor.send({ type: "auth.logout" }),
},
});
// 4. Render
function App() {
return (
<PlayUIProvider actor={actor} registryResult={registryResult}>
<PlayRenderer />
</PlayUIProvider>
);
}

Usage

PlayUIProvider is the standard entry point. It wraps ActorProvider and JSONUIProvider into one composite provider. PlayRenderer is a leaf component without props. It reads the view context and renders the current spec.

import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";
// actor, registryResult from the Quick Start above
<PlayUIProvider
actor={actor}
registryResult={registryResult}
fallback={<div>Loading…</div>}
onError={(err) => console.error(err)}
navigate={navigateFn} // optional: your navigation callback, passed to JSONUIProvider
validationFunctions={valFns} // optional: your form validation helpers
>
<PlayRenderer />
</PlayUIProvider>;

ActorProvider (low-level)

For library authors who need direct control over provider composition:

import { ActorProvider, PlayRenderer } from "@xmachines/play-solid";
// actor, registryResult from the Quick Start above
<ActorProvider actor={actor} registryResult={registryResult}>
<PlayRenderer />
</ActorProvider>;

useActor hook

Access the raw actor instance anywhere inside an ActorProvider or PlayUIProvider tree:

import { useActor } from "@xmachines/play-solid";
function SubmitButton() {
const actor = useActor();
return <button onClick={() => actor.send({ type: "SUBMIT" })}>Submit</button>;
}

usePlayView hook

Access the resolved view context (spec, handlers, registry, store) from within the provider tree:

import { usePlayView } from "@xmachines/play-solid";
import { Renderer } from "@xmachines/json-render-solid";
const MyRenderer = () => {
const view = usePlayView();
return <Renderer spec={view.spec} registry={view.registry} />;
};

Read the fields where you use them, and destructure them not. spec, fallback and loading are GETTERS on the context object. The provider keeps the subtree across an emission that holds the viewKey, so it publishes a new spec through the getter instead of building the tree again — which is what keeps the focus and the caret of an input alive while a stream writes into the view.

// ✅ The Renderer reads `view.spec` inside its own tracking scope, so it follows.
const view = usePlayView();
return <Renderer spec={view.spec} registry={view.registry} />;
// ❌ The value freezes at the first render, and the screen stops following the actor.
const { spec } = usePlayView();

This changed in 2.3.0. Before it, every emission built the subtree again, so a value that a component read one time was fresh because the component itself was new.

API Summary

Components

ExportDescription
PlayUIProviderThe composite provider. Use it as the standard entry point
PlayRendererThe leaf component without props. It renders the current view spec inside a provider tree
ActorProviderThe low-level provider for a custom provider composition

Hooks

ExportDescription
useActor()Returns the raw AnyPlayActor instance from the context. It throws outside a provider tree
usePlayView()Returns the current ViewContextValue (spec, handlers, registry, store). Read the fields where you use them: spec, fallback and loading are getters. It throws outside the tree

Context

ExportDescription
ActorContextThe SolidJS context of the actor. Use ActorContext.Provider directly for a custom composition

Re-exports from @xmachines/json-render-solid

This package re-exports the complete @xmachines/json-render-solid public API, so that a consumer does not need a direct dependency:

import {
// Providers
JSONUIProvider,
StateProvider,
ActionProvider,
VisibilityProvider,
ValidationProvider,
// Renderer
Renderer,
// Registry factory + hooks
defineRegistry,
useBoundProp,
useStateBinding,
useStateValue,
useStateStore,
useActions,
useAction,
useIsVisible,
useFieldValidation,
useOptionalValidation,
useVisibility,
} from "@xmachines/play-solid";

Key Types

TypeDescription
PlayUIProviderPropsProps for PlayUIProvider
ActorProviderPropsProps for ActorProvider
ViewContextValueShape of the context value from usePlayView()
AnyPlayActorPlayActor — the bare actor type that the context providers accept

License

MIT

@xmachines/play-solid - SolidJS renderer for the XMachines Play architecture

This package is the SolidJS rendering layer. It observes the actor atoms and renders the UI components through @xmachines/json-render-solid. SolidJS reactivity only triggers the re-render. The atoms are the source of truth.

Primary entry point:

import { PlayUIProvider, PlayRenderer, defineRegistry } from "@xmachines/play-solid";

For a custom provider composition:

import { ActorProvider, ActorContext, usePlayView } from "@xmachines/play-solid";

Interfaces

Type Aliases

Variables

Functions