Explore, navigate, and visualize your XState machines without leaving VS Code. xState Devtools turns your state machines into an interactive outline and a live statechart diagram you can simulate and generate test paths from — all by analyzing your source statically, with no need to run your app. Works with XState v4 and v5, in JavaScript and TypeScript.

Quick start
- Install the extension and open a JavaScript/TypeScript project that uses XState.
- Click the xState Devtools icon in the activity bar to open the Search and Outline views.
- Click any node to jump to its source, or use View State Diagram to open a machine as a statechart.
No configuration required — machines are discovered automatically as you edit.
Features
🌲 Interactive outline
- Workspace scanning — finds every XState machine in your project and updates live as you type
- Full machine tree — nested & parallel states, transitions, targets, entry/exit/transition actions, guards, invokes, actors, delays, context, and XState v5
setup implementations
- Smart navigation — click to jump to source; double-click an action or guard to jump to its implementation, or a transition target to jump to the destination state (or press F12)
- Cursor sync — the tree highlights the node matching your editor cursor
- Transitions panel — select a state to see every edge touching it, the icon showing direction:
← incoming (a transition that leads in) and → outgoing (a transition out). Click any row to jump to the other state — it becomes the selected state and the panel updates to show its transitions, so you can walk the machine edge by edge
- Search — a dedicated search box (styled like the Extensions view) with keyboard navigation and a type filter (the funnel button): pick one or more node types (states, actions, guards, actors…) to list every node of that type across the scope — even before you type — then narrow with a query. The chip counts facet over the current matches while you search. Use the view's ⋯ menu to order results by relevance, name, or type, or to turn on fuzzy match (e.g.
kp → keep, best matches first)
- CodeLens — each
createMachine gets an inline ▶ View Diagram action plus live state / transition / problem counts, right above the code (toggle with xstateOutline.codeLens)
✏️ Editing & authoring
- Context-aware autocomplete — suggests valid machine, state, transition, invoke, and setup properties, plus valid target / action / guard / actor references
- Tree editing — add, rename, and delete states, transitions, and references, and set a state's or machine's
description, straight from the outline
- Copy / cut / paste — right-click (or Ctrl/Cmd+C/X/V with the outline focused) any editable node to copy or cut it; paste onto a machine or state to drop it in (a copied state brings its whole subtree). Paste lands a state into
states, a transition into on, a context property into context, and an action/guard/actor/delay into the machine's setup() — colliding names prompt for a rename. The copied snippet is also placed on the system clipboard, so you can paste it straight into code
- Diagram editing — the same edit actions are available right from the diagram: right-click a state or the machine to edit/add/delete, set its
description, or add children, transitions, and references; right-click a transition's event to jump to its source, edit it, add an action/guard, or delete it
- Invalid property highlighting — unknown config properties show up in the tree with a red error icon
🚦 Errors pane
A dedicated Errors view collects every problem the static analyzer finds across your machines — also shown inline as editor squiggles.
- What it catches — unreachable / orphaned states, unknown action / guard / actor references (not defined in
setup()), duplicate explicit ids, invalid config properties, the deprecated cond, and unused setup entries
- Real vs. soft — genuine runtime failures (unknown references, duplicate ids) are errors; static heuristics (unreachable state, invalid property) are warnings; deprecations and unused-setup entries are informational
- Severity filter — show all, warnings & errors (the default), or errors only to focus on what actually fails at runtime
- Grouping — organize problems by file, by severity, or as a flat list, from the title-bar icons
- Navigate & copy — click a problem to jump to its source; copy a single issue or a whole group (right-click or Ctrl/Cmd+C)
- At a glance — a badge shows the total count, and the pane follows the outline's file/workspace scope
🗺️ Statechart diagram
- Visual statechart — render any machine (or any compound state) as a Harel-style diagram in its own editor tab via View State Diagram (from the outline or by right-clicking a machine/state in the code)
- Every transition kind —
on events, after (delayed), always (transient), invoke onDone/onError, and state-level onDone, with EVENT [guard] / actions labels; internal (action-only) transitions and invoked services (invoke <src>) show inside the state box — an invoked machine found in the workspace is nested inline as expandable children of the invoke state (expand its + to drill in, and while debugging its active states light up from the live child actor); an invoke whose machine can't be resolved statically falls back to right-click → Open invoked machine
- Automatic layout — clean hierarchical layout with smooth, curved transitions; a parallel state whose regions are independent (no transitions between them) wraps into a grid rather than one ever-widening row, so wide machines stay readable
- Top-down ↔ left-right — a sensible direction is chosen from the machine's shape (left-right for linear machines, top-down for parallel/large ones); flip it anytime and your choice is remembered
- Live updates — the diagram refreshes as you edit the source, preserving your pan & zoom, and auto-centers when first opened
- Pan, zoom & fit (smoothly eased), plus actual size (100%) and expand all / collapse all. Selecting a state that's off-screen pans it into view; the zoom buttons/keys centre on the selected state (or, in the simulator, the active state); collapsing/expanding recenters the diagram (until you take manual control of the view)
- Keyboard navigation — arrow keys move the selection between states, Enter expands/collapses a compound or jumps a state to its source, Shift+arrows pan,
]/[ (or +/-) zoom, 0/. fit, and 1 actual size
- Reflects the outline — collapsed states render as single blocks; click a collapsed block to expand it, or a region's title bar to collapse it
- Two-way sync — click a state to select it in the tree (click the empty body of a compound/parallel state to select that parent; its header toggles collapse), click an event to select the state it leads to (panned into view), and selecting in the tree (or the editor cursor) highlights it in the diagram; a state's
description shows as a hover tooltip
- Hover to focus — hovering a state emphasizes its transitions and dims the rest
- Export the diagram as SVG, PNG, or Mermaid (
stateDiagram-v2 — drop it straight into Markdown/docs; also from the right-click menu of any machine/state in the outline)
- Interactive simulator — hit ▷ Sim to walk the machine: the active state configuration lights up green, every enabled transition becomes a button (each guarded branch its own choice, since guards can't be evaluated statically), and a trace records your path with step-back and reset. The diagram centres when the simulator opens; as you step, the active state is kept on screen and selected in the outline; click any active-state chip or trace row to jump to that state. Correctly enters/exits compound and parallel regions;
after/always/onDone are surfaced as explicitly fireable events
- Test paths & coverage — right-click a machine → Generate Test Paths for a Markdown coverage report (shortest path to every reachable state, unreachable states flagged) with copy-paste test skeletons
- Generate Setup Stubs — right-click a machine → scaffold a
setup({ actions, guards, actors, delays }) block with a typed stub for every implementation the machine references, each flagged as already in setup() or missing from it (XState built-ins like assign/not are skipped)
- Focus mode — open the diagram on a compound state to see just that subtree
🐞 Live debugger — now a separate extension
The live debugger (attach to a running app, inspect instances/context, time-travel the event log, overlay live state on the diagram) has moved to its own standalone extension, XState Debugger. Install it alongside this one for runtime inspection; it bundles its own diagram, so it also works on its own.
The extension contributes Language Model Tools so VS Code's AI agent can reason about your machines (requires VS Code ≥ 1.95). Let the agent call them, or #-reference them in chat by name: #xstateMachines (list machines), #xstateDescribe (states + transitions as compact JSON), #xstateDiagram (Mermaid), #xstateTestPaths (shortest path to each state + unreachable states), #xstateValidate (XState diagnostics), #xstateFindImpl (locate where an action/guard/service is defined), #xstateFindRefs (where a name is used as an action/guard/actor/event), #xstateSetupCoverage (which actions/guards/actors a machine references and which are missing from setup()), #xstateEvents (a machine's send() API), and #xstateStateDetail (one state's entry/exit/invokes + outgoing transitions). Outputs are kept terse to minimise agent token usage. The same analysis is available outside the editor via the standalone @xstate-devtools/mcp server.
Reading the diagram

The diagrams intentionally follow Harel statechart conventions — the same notation XState itself is based on. Initial-state arrows, nested regions, dashed boundaries for parallel (orthogonal) states, and EVENT [guard] / action transition labels are all standard Harel/SCXML notation, so the diagrams read the way a statechart is meant to.
The example above shows a parallel checkout machine: the dashed outer box with the parallel tag runs its two regions — payment and fulfilment — concurrently, each with its own initial state (the filled dot), entry/exit actions inside the boxes, guarded transitions like ADDRESS_VALID [inDeliveryZone], and final states (double outline).
| You'll see |
Meaning |
| Rounded box |
A state |
| Filled dot → state |
The initial state of a region |
| Box with a double outline |
A final state |
Dashed box with a parallel tag |
A parallel (orthogonal) state — its regions run concurrently |
Box with a ⊞ (plus) square |
A collapsed compound state (click to expand) |
Region with a ⊟ (minus) title bar |
An expanded region (click the title bar to collapse) |
| Outer titled box |
The machine itself |
| Curved arrow with a label |
A transition, labelled EVENT [guard] / actions |
entry/ … and exit/ … inside a box |
The state's entry/exit actions |
Diagram toolbar: zoom out −, the live zoom-percent button (click to reset to 100%), zoom in +, fit ⊡, toggle direction ↧/↦, expand all ⊞, collapse all ⊟, toggle internal-transition rows /ƒ, export SVG / PNG / MMD (Mermaid), and ▷ Sim to enter the interactive simulator.
Right-click anywhere in the diagram for a context menu — Go to Source and Expand/Collapse for the state under the cursor, the full set of editing actions (edit/add child/add transition/add reference/set description/delete on a state, the applicable subset on the machine, and go to source/edit/add action-guard/delete on a transition's event), plus fit, actual size, expand/collapse all, toggle internal transitions, and export.
Diagram keyboard shortcuts: arrow keys move the selection between states, Enter expands/collapses the selected compound (or jumps a leaf state to its source), Shift+arrows pan, ]/[ (or +/-) zoom, 0 or . fits to screen, and 1 resets to actual size.
Icon & color legend
State nodes use custom Harel-statechart shapes — the same notation as the diagram — in a neutral grey with light/dark variants. Every other icon and color is drawn from your active VS Code theme, so the extension looks at home in light, dark, and high-contrast themes alike.
| Symbol |
Meaning |
| 📦 Blue box |
Machine |
| ○ Hollow circle |
State |
| ● Filled dot |
Initial state |
| ◉ Bullseye (double circle) |
Final state |
| ▭ Dashed-split box |
Parallel (orthogonal) state |
| Ⓗ Circled H |
History state |
| 🟠 Orange event |
Transition (orange inbox = an on handler group; ⚡ always, 🕐 after) |
| 🎯 Magenta target |
Transition target |
| 🚀 Action · ⤓ entry · ⤒ exit |
Actions (entry/exit/transition) |
| 🛡️ Cyan shield |
Guard |
| ∧ · ∨ · ¬ cyan badge |
Guard combinator group (and/or/not), with each inner guard nested beneath |
| 🔌 Yellow board |
Invoke · 👤 actor |
| 🕘 Delay · ⚙️ setup section |
Timing & v5 setup blocks |
| 🔤 Context · 🔧 context property |
Machine context |
| ❌ Red error |
Invalid / unknown property |
In the diagram, state borders, arrows, and labels follow your editor's foreground color; state fills use the editor widget background; a selected state is filled with your theme's selection color and outlined with the focus color. The initial dot follows the foreground color (a solid black dot on light themes, per Harel convention); a final state gets a second inner outline in the foreground color (the Harel double border), and parallel states use a blue dashed border.
Supported patterns
The extension detects statically defined machines in these forms:
XState v4
const machine = createMachine({ ... });
const machine = Machine({ ... });
XState v5
const machine = setup({ ... }).createMachine({ ... });
const machine = journeySetup.createMachine({ ... });
const config = createStateConfig({ ... });
const config = stateConfig({ ... });
It also surfaces v5 setup sections:
const machine = setup({
actions: { saveDraft: () => {} },
guards: { canSubmit: () => true },
actors: { loadUser: fromPromise(async () => {}) },
delays: { RETRY_DELAY: 1000 }
}).createMachine({ ... });
Tips
- The Outline title bar has toggles for scope (file/workspace), view mode (grouped/flat), state configs, cursor following, navigation target (clicking a node jumps to code or focuses it in the diagram), whether the diagram reflects tree expansion, and child grouping (flat, or nested under type categories — States, Events, Actions, Guards, Invoked, Context…).
- Right-click any tree node for editing actions and Go to Implementation.
- In Search, use ↑/↓/Enter to move through results. Click the funnel button to pick node types — selecting a type lists all of those nodes immediately (no query needed); typing then narrows within the selected types. The ⋯ menu orders results by relevance, name, or type, and toggles fuzzy matching.
Keyboard shortcuts
| Where |
Keys |
Action |
| Outline (focused) |
F12 |
Go to Implementation for the selected action / guard / target |
| Outline (focused) |
Ctrl/Cmd+C / X / V |
Copy / cut / paste the selected node |
| Errors pane (focused) |
Ctrl/Cmd+C |
Copy the selected problem(s) or group |
| Search |
↑ / ↓ / Enter |
Move through results and open the selected one |
| Diagram |
arrow keys |
Move the selection between states |
| Diagram |
Enter |
Expand/collapse the selected compound, or jump a leaf state to its source |
| Diagram |
Shift+arrows |
Pan |
| Diagram |
] / [ (or + / -) |
Zoom in / out |
| Diagram |
0 or . |
Fit to screen |
| Diagram |
1 |
Reset to actual size (100%) |
The F12 and Ctrl/Cmd+C bindings are contributed defaults — rebind them in VS Code's Keyboard Shortcuts if you prefer.
Settings
| Setting |
Default |
Description |
xstateOutline.defaultScope |
workspace |
Scan the current file only, or the whole workspace |
xstateOutline.defaultViewMode |
flat |
Flat list of machines, or grouped by file |
xstateOutline.showStateConfigs |
false |
Include createStateConfig/stateConfig patterns in the outline |
xstateOutline.followCursor |
true |
Reveal the tree node matching the editor cursor (surfaces a hidden state config for the session if the cursor lands inside one) |
xstateOutline.navTarget |
code |
Where clicking a tree node navigates: code (jump to source) or diagram (focus the state in the diagram). Toggle from the outline title bar |
xstateOutline.graphReflectsTreeExpansion |
true |
Make the diagram render only the states expanded in the outline |
xstateOutline.codeLens |
true |
Show a CodeLens (counts + "View Diagram") above each machine |
xstateOutline.showInternalTransitions |
true |
Show internal (action-only) transitions inside state boxes in the diagram (toggle live from the diagram toolbar) |
xstateOutline.childGrouping |
flat |
How a state/machine's children are arranged: flat, or by-type (nested under collapsible category nodes — States, Events, Actions, Guards, Invoked, Context…). In by-type, a setup() node's implementations also split into Actions / Guards / Actors / Delays |
xstateOutline.errorsGrouping |
file |
How the Errors pane groups problems: file, severity, or flat |
xstateOutline.errorsFilter |
warning |
Minimum severity shown in the Errors pane: all, warning (warnings & errors), or error |
Requirements
- Visual Studio Code 1.78.0 or higher
- A JavaScript or TypeScript project using XState
Known limitations
- Detects statically defined machines only (not dynamically generated ones)
- Context values are shown one level deep to avoid clutter
- Tree editing focuses on common object/string forms; advanced shapes may still need manual source edits
- In a focused sub-diagram, transitions whose target lies outside the selected subtree point to a faded stub labelled with that target
- The simulator and test paths are structural: guards and
after delays aren't evaluated (you pick each branch yourself), and history states restore structurally only — a reported path is a possible route, not a guard-validated one
Credits & acknowledgements
This extension exists because of XState and the team at Stately. XState is a wonderful, rigorous library — it brings real statecharts to JavaScript and TypeScript, with first-class support for hierarchy, parallel regions, guards, actors, and more, all grounded in decades-old, battle-tested theory. It makes complex application logic predictable, testable, and a genuine pleasure to work with.
xState Devtools is an independent, community project and is not affiliated with or endorsed by Stately. All credit for XState, the statechart model, and the broader ecosystem belongs to Stately and the XState contributors. If you build state machines, do yourself a favor and explore the official tooling too:
Links
License
MIT — and with gratitude to Stately and the XState community.
| |