| license | Apache-2.0 |
| name | state-machine-designer |
| description | Design and implement finite state machines and statecharts for complex UI flows using XState v5 and Zustand. Activate on: multi-step forms, complex UI state, wizard flows, auth flows, statechart, XState. NOT for: simple boolean toggles (use React useState), server state (use data-fetching-strategist). |
| allowed-tools | Read,Write,Edit,Bash(npm:*,npx:*) |
| category | Frontend & UI |
| tags | ["state-machines","xstate","zustand","statecharts"] |
| pairs-with | [{"skill":"form-validation-architect","reason":"Multi-step form flows need both state machines and validation logic"},{"skill":"error-boundary-strategist","reason":"Error states in machines map to recovery UI via error boundaries"}] |
State Machine Designer
Model complex UI flows as finite state machines and statecharts using XState v5, eliminating impossible states and race conditions.
Activation Triggers
Activate on: multi-step wizards, authentication flows, payment checkout, complex form state, drag-and-drop orchestration, media player controls, XState, statechart, createMachine.
NOT for: simple boolean toggles (use useState). Server/async data caching -- use data-fetching-strategist. Global app state without transitions -- use Zustand directly.
Quick Start
- Map states -- list every discrete state the UI can be in (idle, loading, error, success, etc.).
- Define events -- list every user action or system event that triggers transitions.
- Draw the statechart -- use Stately.ai visual editor or ASCII diagram to verify no impossible transitions.
- Implement with XState v5 --
setup() + createMachine() with @xstate/react useMachine hook.
- Test transitions -- use
createActor in unit tests to verify every path.
Core Capabilities
| Domain | Technologies | Key Patterns |
|---|
| State Machines | XState v5, setup() API | Flat machines for simple flows |
| Statecharts | XState hierarchical/parallel states | Nested states, history states |
| UI Binding | @xstate/react useMachine, useSelector | React integration, selective re-renders |
| Lightweight State | Zustand with state enum pattern | When XState overhead is too much |
| Visualization | Stately.ai editor, @stately-ai/inspect | Visual debugging of live machines |
| Testing | createActor, getSnapshot | Deterministic transition testing |
Architecture Patterns
Pattern 1: XState v5 Machine with setup()
import { setup, assign } from 'xstate';
const checkoutMachine = setup({
types: {
context: {} as {
items: CartItem[];
address: Address | null;
paymentMethod: PaymentMethod | null;
error: string | null;
},
events: {} as
| { type: 'SET_ADDRESS'; address: Address }
| { type: 'SET_PAYMENT'; method: PaymentMethod }
| { type: 'SUBMIT' }
| { type: 'BACK' }
| { type: 'RETRY' },
},
guards: {
hasAddress: ({ context }) => context.address !== null,
hasPayment: ({ context }) => context.paymentMethod !== null,
},
}).createMachine({
id: 'checkout',
initial: 'cart',
context: { items: [], : , : , : },
: {
: { : { : { : , : } } },
: { : { : { : ({ : e. }), : }, : } },
: { : { : { : ({ : e. }), : }, : } },
: { : { : , : } },
: {
: { : , : , : { : , : ({ : e.. }) } },
},
: { : },
: { : { : , : } },
},
});
Pattern 2: Zustand State Enum (Lightweight Alternative)
When XState is overkill but useState booleans create impossible states:
import { create } from 'zustand';
type AuthState = 'idle' | 'authenticating' | 'authenticated' | 'error' | 'mfa_required';
interface AuthStore {
state: AuthState;
user: User | null;
error: string | null;
login: (creds: Credentials) => Promise<void>;
submitMfa: (code: string) => Promise<void>;
logout: () => void;
}
const useAuthStore = create<AuthStore>((set, get) => ({
state: 'idle',
user: null,
error: null,
login: async (creds) => {
if (get().state !== 'idle' && get().state !== ) ;
({ : , : });
{
res = api.(creds);
(res.) ({ : });
({ : , : res. });
} (e) {
({ : , : e. });
}
},
: ({ : , : }),
}));
Pattern 3: Statechart Visualization
┌────────────────────────────────────────────────┐
│ checkout │
│ │
│ [cart] ──SUBMIT──> [address] ──SET_ADDR──> │
│ ^ │ │
│ └───BACK───────────┘ │
│ │
│ [payment] ──SET_PAY──> [review] ──SUBMIT──> │
│ ^ │ │
│ └───BACK───────────────┘ │
│ │
│ [processing] ──onDone──> [success] (final) │
│ │ │
│ └──onError──> [error] ──RETRY──> │
│ │ (back to processing)│
│ └──BACK──> [review] │
└────────────────────────────────────────────────┘
Anti-Patterns
- Boolean soup --
isLoading && !isError && isSubmitted creates impossible state combinations. Use a single state enum or machine instead.
- Skipping the statechart diagram -- jumping to code without visualizing transitions guarantees missing edge cases. Draw first, code second.
- Putting side effects in guards -- guards must be pure predicates. Use
actions or invoke for side effects.
- Over-engineering simple toggles -- a modal open/close does not need XState. Use
useState<boolean> for trivially simple state.
- Forgetting to handle the error-to-retry path -- users get stuck in error states with no way back. Always define recovery transitions.
Quality Checklist