Inspector
@xstate/inspect
enables you to inspect and manipulate machines while they’re running in your app. Check out the @xstate/inspect package on GitHub.
Currently, @xstate/inspect
only works in frontend applications , but we’re working on a version that can inspect machines running in Node.
To see it running right now, here are our CodeSandbox templates:
Quickstart
- Install with npm or yarn:
npm install @xstate/inspect
# or yarn add @xstate/inspect
- Import @xstate/inspect 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 createActored machines you want to visualize:
import { createMachine } from 'xstate';
const someMachine = createMachine({});
import { createActor } from 'xstate';
import { inspect } from '@xstate/inspect';
const actor = createActor(someMachine, {
devTools: true,
}).start();
Options
// defaults
inspect({
iframe: () => document.querySelector('iframe[data-xstate]'),
url: 'https://stately.ai/viz?inspect',
});
// the code above does the same as:
inspect();
You can pass several properties to the options
object, all of which are optional.
iframe
iframe
(function or iframe Element
or false
) resolves to the iframe
element to display the inspector. If set to iframe: false
, a popup window is used instead.
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
Use the url
property (string) to specify the URL of the inspector you want to connect to. By default, the inspector runs on https://stately.ai/viz?inspect
.
Disconnecting
inspect
returns a function, disconnect
, you can use to disconnect the inspector.
import { inspect } from '@xstate/inspect';
const inspector = inspect();
inspector?.disconnect();
Implementation
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:
"actor.register"
{
type: 'actor.register';
machine: AnyStateMachine;
state: AnyState;
id: string;
sessionId: string;
parent?: string;
source?: string;
}
"actor.stop"
{
type: 'actor.stop';
sessionId: string;
}
"actor.state"
{
type: 'actor.state';
state: AnyState;
sessionId: string;
}
"actor.event"
{
type: 'actor.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 "actor.*" events
console.log(event);
});
You can also send events to the receiver:
// ...
// This will send the event to the inspected actor
windowReceiver.send({
type: 'xstate.event',
event: JSON.stringify({ type: 'someEvent' }),
actor: /* session ID of the actor this event is sent to */
});
The typical inspection workflow is as follows:
- The
inspect(/* ... */)
call on the client opens the inspector. For example, in a separate window or creates a WebSocket connection. - The receiver sends an
"xstate.inspecting"
event to the client. - The client sends
"actor.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 rendered visually, and its current state,
event.state
, is highlighted - As the actor at the source receives events and changes state, it will send the receiver
"actor.event"
and"actor.state"
events, respectively. - The inspector can use those events to highlight the current state and keep a log of events sent to that actor.
- When the actor stops, a
"actor.stop"
event is sent to the receiver with theevent.sessionId
to identify the stopped actor.
How do I run the inspector in a NextJS app?
If you want to run the inspector in a NextJS app, you must ensure that the inspector code only runs on the client rather than the server:
if (typeof window !== 'undefined') {
inspect({
/* options */
});
}