From harness-claude
Connects XState machines to React components using useMachine, useActor, and useSelector hooks for state-driven UI and shared machine instances via context.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:xstate-react-integrationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Connect XState machines to React components with useMachine, useActor, and useSelector hooks
Connect XState machines to React components with useMachine, useActor, and useSelector hooks
useMachine(machine) to create and interpret a machine instance local to a component.[state, send, actorRef] from the hook. state contains the current state; send dispatches events.state.matches('stateName') for conditional rendering — never compare state.value as a string directly (it can be an object for compound states).state.context.useActorRef in a parent and pass it down via React context. Children use useSelector to read specific state slices.useSelector with a comparison function to prevent unnecessary re-renders.// LoginForm.tsx
import { useMachine } from '@xstate/react';
import { authMachine } from './auth.machine';
function LoginForm() {
const [state, send] = useMachine(authMachine, {
// Provide service implementations
services: {
authenticateUser: async (ctx, event) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email: event.email, password: event.password }),
});
if (!res.ok) throw new Error('Invalid credentials');
return res.json();
},
},
});
if (state.matches('authenticated')) {
return <div>Welcome, {state.context.user?.name}</div>;
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
send({ type: 'LOGIN', email: data.get('email') as string, password: data.get('password') as string });
}}
>
<input name="email" type="email" />
<input name="password" type="password" />
<button disabled={state.matches('authenticating')}>
{state.matches('authenticating') ? 'Signing in...' : 'Sign in'}
</button>
{state.matches('error') && <p>{state.context.error}</p>}
</form>
);
}
// Shared machine via React context
import { createActorContext } from '@xstate/react';
import { appMachine } from './app.machine';
const AppMachineContext = createActorContext(appMachine);
function App() {
return (
<AppMachineContext.Provider>
<Header />
<Content />
</AppMachineContext.Provider>
);
}
function Header() {
const userName = AppMachineContext.useSelector(
(state) => state.context.user?.name
);
return <header>{userName ?? 'Guest'}</header>;
}
useMachine vs useActorRef: useMachine creates a new actor on mount and re-renders the component on every state change. useActorRef creates the actor but does NOT cause re-renders — combine with useSelector for surgical updates.
state.matches deep matching: For compound states, state.matches('playing.fastForward') checks nested states. For parallel states, pass an object: state.matches({ bold: 'on', italic: 'off' }).
useSelector for performance: Instead of re-rendering on every state change, select only what you need:
const isLoading = AppMachineContext.useSelector((state) => state.matches('loading'));
// Component only re-renders when isLoading changes
XState v5 with @xstate/react v4:
import { useMachine, useActorRef, useSelector } from '@xstate/react';
// useMachine still works the same
const [snapshot, send] = useMachine(machine);
// snapshot.value, snapshot.context, snapshot.matches() work identically
Testing components with machines: Pass a pre-configured machine or use @xstate/react/lib/test utilities. Override services to return mock data.
Common mistakes:
state.value === 'loading' instead of state.matches('loading') — breaks for compound statesuseMachine — the machine runs but invocations fail silentlyhttps://stately.ai/docs/xstate-react
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeDefines statecharts with XState's createMachine for explicit states, transitions, context, and events. Useful for modeling complex UI flows and preventing illegal state transitions.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.