Automated summary
Explains how to use XState's invoke to run promises, callbacks, observables, and child machines bound to a state node's lifecycle.
Strengths
- Covers both promise and callback services with concrete, runnable examples (HTTP fetch, WebSocket)
- Maps v4 services to v5 actors using fromPromise and fromCallback helpers
- Shows onDone/onError handling combined with assign to update context
- Clarifies the lifecycle: starts on state entry, cancels or cleans up on exit
Limitations
- The v5 example is cut off, and observable and child-machine cases lack full code
- Assumes prior familiarity with TypeScript and core XState concepts
- Does not cover advanced error recovery or retry strategies beyond a simple RETRY event
Reach for it when wiring async work (API calls, sockets, timers, sub-workflows) into a statechart that needs clean entry/exit handling.
Skip it if you're not using XState, or if you need a general introduction to state machines rather than the invoke pattern.
Security analysis
SafeThis is a reference document about XState patterns with no declared tools or executable instructions. It contains only TypeScript examples and documentation, so it poses no meaningful execution or exfiltration risk.
No concerns found
How this analysis is madeExamples
Refactor my loading state so the API call uses XState invoke with onDone and onError, storing the result and error in context via assign.Write an XState callback service used with invoke that opens a WebSocket, sends incoming messages back to the machine, and closes the socket when the state exits.Convert my machine's services option into XState v5 actors using setup() with fromPromise and fromCallback.XState Invoke Pattern
Invoke promises, callbacks, observables, and child machines as services tied to state node lifecycles
When to Use
- Fetching data when entering a state (API calls, database queries)
- Running long-lived processes tied to a state (WebSocket connections, timers)
- Delegating a sub-workflow to a child machine
- Any async operation that should start on state entry and cancel on state exit
Instructions
- Use
invokeinside a state node. The service starts when the state is entered and is automatically stopped when the state is exited. - Handle results with
onDone(success) andonError(failure) on the invoke config. - For promises, the resolved value becomes
event.datainonDone. The rejected value becomesevent.datainonError. - For callbacks, return a cleanup function — XState calls it when the state is exited.
- Define services in the machine options under
services(v4) oractors(v5) to keep machines testable and serializable. - Use
input(v5) orwithContext(v4) to pass data to invoked machines.
// data-fetch.machine.ts
import { createMachine, assign } from 'xstate';
interface FetchContext {
url: string;
data: unknown | null;
error: string | null;
}
type FetchEvent = { type: 'RETRY' } | { type: 'REFRESH' };
const fetchMachine = createMachine<FetchContext, FetchEvent>(
{
id: 'fetch',
initial: 'loading',
context: { url: '', data: null, error: null },
states: {
loading: {
invoke: {
id: 'fetchData',
src: 'fetchService',
onDone: {
target: 'success',
actions: assign({ data: (_, event) => event.data, error: null }),
},
onError: {
target: 'failure',
actions: assign({ error: (_, event) => event.data.message }),
},
},
},
success: {
on: { REFRESH: 'loading' },
},
failure: {
on: { RETRY: 'loading' },
},
},
},
{
services: {
fetchService: (ctx) =>
fetch(ctx.url).then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
}),
},
}
);
// Callback service — WebSocket connection
const chatMachine = createMachine(
{
id: 'chat',
initial: 'connected',
states: {
connected: {
invoke: {
id: 'websocket',
src: 'connectWebSocket',
},
on: {
MESSAGE_RECEIVED: { actions: 'addMessage' },
DISCONNECT: 'disconnected',
},
},
disconnected: { type: 'final' },
},
},
{
services: {
connectWebSocket: (ctx) => (sendBack, onReceive) => {
const ws = new WebSocket(ctx.wsUrl);
ws.onmessage = (msg) => sendBack({ type: 'MESSAGE_RECEIVED', data: msg.data });
// onReceive listens for events sent TO this service
onReceive((event) => {
if (event.type === 'SEND_MESSAGE') ws.send(event.text);
});
// Cleanup — called when state exits
return () => ws.close();
},
},
}
);
Details
Service types in v4:
- Promise — resolves once, triggers
onDoneoronError - Callback —
(sendBack, onReceive) => cleanup— long-lived, bidirectional - Observable — emits events over time, completes triggers
onDone - Machine — child statechart,
onDonefires when child reaches final state
XState v5 equivalents: Services become actors defined with helper functions:
import { fromPromise, fromCallback } from 'xstate';
const machine = setup({
actors: {
fetchService: fromPromise(async ({ input }: { input: { url: string } }) => {
const res = await fetch(input.url);
return res.json();
}),
wsService: fromCallback(({ sendBack, input }) => {
const ws = new WebSocket(input.url);
ws.onmessage = (msg) => sendBack({ type: 'MESSAGE_RECEIVED', data: msg.data });
return () => ws.close();
}),
},
}).createMachine({
/* ... */
});
Automatic cancellation: When the state that owns the invoke exits, XState automatically stops the service. For promises, the AbortController.signal is not used automatically — you must wire it yourself if needed. For callbacks, the cleanup function runs.
Multiple invocations: A state can have multiple invoke entries (use an array). All start on entry and stop on exit.
Testing: Replace services with mocks in tests by passing { services: { fetchService: mockService } } to interpret(machine.withConfig({ services: ... })).
Source
https://stately.ai/docs/invoke
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/xstate-invoke-pattern-mrx3xz77/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/xstate-invoke-pattern-mrx3xz77' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/xstate-invoke-pattern-mrx3xz77/download' --output '.skill-downloads/xstate-invoke-pattern-mrx3xz77/SKILL.md.part' && mv '.skill-downloads/xstate-invoke-pattern-mrx3xz77/SKILL.md.part' '.skill-downloads/xstate-invoke-pattern-mrx3xz77/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
This file does not provide a usable name/description header for this native procedure. It remains available to download and inspect; use the manual method below.
Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/xstate-invoke-pattern-mrx3xz77/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.