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:
bash
npm install @xstate/inspect# or yarn add @xstate/inspect
bash
npm install @xstate/inspect# or yarn add @xstate/inspect
- Import @xstate/inspect at the beginning of your project before any other code is called:
ts
import {inspect } from "@xstate/inspect";inspect ({// options// url: 'https://stately.ai/viz?inspect', // (default)iframe : false, // open in new window});
ts
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:
ts
import {interpret } from "xstate";import {inspect } from "@xstate/inspect";constservice =interpret (someMachine , {devTools : true,}).start ();
ts
import {interpret } from "xstate";import {inspect } from "@xstate/inspect";constservice =interpret (someMachine , {devTools : true,}).start ();
Options
js
// defaultsinspect({iframe: () =>document.querySelector("iframe[data-xstate]"),url: "https://stately.ai/viz?inspect",});// the code above does the same as:inspect();
js
// defaultsinspect({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.
⚠️ You may need to “allow popups” in your browser to display the inspector in a popup window, as the browser might block them 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:
js
// eagerinspect({iframe: document.querySelector("iframe.some-xstate-iframe",),});
js
// eagerinspect({iframe: document.querySelector("iframe.some-xstate-iframe",),});
js
// lazyinspect({iframe: () =>document.querySelector("iframe.some-xstate-iframe"),});
js
// lazyinspect({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.
ts
import {inspect } from "@xstate/inspect";constinspector =inspect ();inspector ?.disconnect ();
ts
import {inspect } from "@xstate/inspect";constinspector =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:
-
"service.register"
ts{type: 'service.register';machine: AnyStateMachine;state: AnyState;id: string;sessionId: string;parent?: string;source?: string;}ts{type: 'service.register';machine: AnyStateMachine;state: AnyState;id: string;sessionId: string;parent?: string;source?: string;} -
"service.stop"
ts{type: "service.stop";sessionId: string;}ts{type: "service.stop";sessionId: string;} -
"service.state"
ts{type: "service.state";state: AnyState;sessionId: string;}ts{type: "service.state";state: AnyState;sessionId: string;} -
"service.event"
ts{type: 'service.event';event: SCXML.Event<any>;sessionId: string};ts{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:
js
import { createWindowReceiver } from "@xstate/inspect";const windowReceiver = createWindowReceiver(/* options? */);windowReceiver.subscribe((event) => {// here, you will receive "service.*" eventsconsole.log(event);});
js
import { createWindowReceiver } from "@xstate/inspect";const windowReceiver = createWindowReceiver(/* options? */);windowReceiver.subscribe((event) => {// here, you will receive "service.*" eventsconsole.log(event);});
You can also send events to the receiver:
js
// ...// This will send the event to the inspected servicewindowReceiver.send({type: 'xstate.event',event: JSON.stringify({ type: 'someEvent' }),service: /* session ID of the service this event is sent to */});
js
// ...// This will send the event to the inspected servicewindowReceiver.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. For example, 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 rendered visually, 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.
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:
js
if (typeof window !== "undefined") {inspect({/* options */});}
js
if (typeof window !== "undefined") {inspect({/* options */});}