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.
Creates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.