# @xstate/inspect
The @xstate/inspect package (opens new window) contains inspection tools for XState.
- XState (Vanilla) (opens new window)
- XState + TypeScript (opens new window)
- XState + Vue (opens new window)
- XState + React (opens new window)
See CodeSandbox example here (opens new window)
# Installation
- Install with npm or yarn:
npm install @xstate/inspect
# or yarn add @xstate/inspect
- Import it at the beginning of your project, before any other code is called:
import { inspect } from '@xstate/inspect';
inspect({
// options
// url: 'https://stately.ai/viz?inspect', // (default)
iframe: false // open in new window
});
- Add
{ devTools: true }
to any interpreted machines you want to visualize:
import { interpret } from 'xstate';
import { inspect } from '@xstate/inspect';
// ...
const service = interpret(someMachine, { devTools: true });
# Inspect Options
// defaults
inspect({
iframe: () => document.querySelector('iframe[data-xstate]'),
url: 'https://stately.ai/viz?inspect'
});
// the above is the same as this:
inspect();
Arguments: the options
object passed to inspect(options)
with the following optional properties:
iframe
(function or iframeElement
orfalse
) - resolves to theiframe
element to display the inspector in. If this is set toiframe: false
, then a popup window will be used instead.⚠️ Note: you might need to allow popups to display the inspector in a popup window, as they might be blocked by the browser by default.
By default, the inspector will look for an
<iframe data-xstate>
element anywhere in the document. If you want to target a custom iframe, specify it eagerly or lazily:// eager inspect({ iframe: document.querySelector('iframe.some-xstate-iframe') });
// lazy inspect({ iframe: () => document.querySelector('iframe.some-xstate-iframe') });
url
(string) - the URL of the inspector to connect to. By default, the inspector is running onhttps://stately.ai/viz?inspect
.
Returns: an inspector object with the following properties:
disconnect
(function) - a function that disconnects the inspector and cleans up any listeners.
# Implementing
You can implement your own inspector by creating a receiver. A receiver is an actor that receives inspector events from a source (like a parent window or a WebSocket connection):
"service.register"
{ type: 'service.register'; machine: AnyStateMachine; state: AnyState; id: string; sessionId: string; parent?: string; source?: string; }
"service.stop"
{ type: 'service.stop'; sessionId: string; }
"service.state"
{ type: 'service.state'; state: AnyState; sessionId: string; }
"service.event"
{ type: 'service.event'; event: SCXML.Event<any>; sessionId: string; }
To listen to events from an inspected source, create a receiver with the appropriate create*Receiver(...)
function; for example:
import { createWindowReceiver } from '@xstate/inspect';
const windowReceiver = createWindowReceiver(/* options? */);
windowReceiver.subscribe((event) => {
// here, you will receive "service.*" events
console.log(event);
});
You can also send events to the receiver:
// ...
// This will send the event to the inspected service
windowReceiver.send({
type: 'xstate.event',
event: JSON.stringify({ type: 'someEvent' }),
service: /* session ID of the service this event is sent to */
});
The typical inspection workflow is as follows:
- The
inspect(/* ... */)
call on the client opens the inspector (e.g., in a separate window, or creates a WebSocket connection) - The receiver sends an
"xstate.inspecting"
event to the client - The client sends
"service.register"
events to the receiver - An inspector listening to the receiver (via
receiver.subscribe(...)
) registers the machine (event.machine
) by itsevent.sessionId
- The machine is visually rendered, and its current state (
event.state
) is highlighted - As the service at the source receives events and changes state, it will send the receiver
"service.event"
and"service.state"
events, respectively - The inspector can use those events to highlight the current state and keep a log of events sent to that service
- When the service stops, a
"service.stop"
event is sent to the receiver with theevent.sessionId
to identify the stopped service.
# FAQs
How do I run the inspector in a NextJS app?
Ensure that the inspector code only runs on the client, rather than the server:
if (typeof window !== 'undefined') { inspect({ /* options */ }); }