What is @xstate/inspect?
@xstate/inspect is a tool for visualizing, inspecting, and debugging XState state machines and statecharts. It provides a way to see the current state, events, and transitions of your state machines in real-time, which can be extremely helpful for development and debugging purposes.
What are @xstate/inspect's main functionalities?
Real-time Inspection
This feature allows you to inspect the state machine in real-time. By using the `inspect` function, you can open a new window or an iframe that shows the current state, events, and transitions of your state machine.
import { inspect } from '@xstate/inspect';
inspect({
iframe: false // open in new window
});
// Then, use the inspect service in your XState machine
import { createMachine, interpret } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const service = interpret(toggleMachine, { devTools: true }).start();
Event Logging
This feature logs events and state transitions to the console, which can be useful for debugging and understanding the flow of your state machine.
import { inspect } from '@xstate/inspect';
inspect({
iframe: false // open in new window
});
import { createMachine, interpret } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const service = interpret(toggleMachine, { devTools: true }).onTransition((state) => {
console.log(state.value);
}).start();
State Visualization
This feature provides a visual representation of your state machine, showing the current state, possible transitions, and events. This can be extremely helpful for understanding and debugging complex state machines.
import { inspect } from '@xstate/inspect';
inspect({
iframe: false // open in new window
});
import { createMachine, interpret } from 'xstate';
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: { on: { TOGGLE: 'active' } },
active: { on: { TOGGLE: 'inactive' } }
}
});
const service = interpret(toggleMachine, { devTools: true }).start();
// The state visualization will be available in the new window or iframe
Other packages similar to @xstate/inspect
redux-devtools
Redux DevTools is a set of tools to help with debugging Redux applications. It provides a way to inspect the state and actions of your Redux store in real-time, similar to how @xstate/inspect works for XState state machines. However, Redux DevTools is specifically designed for Redux and does not support XState state machines.
mobx-devtools
MobX DevTools is a set of tools for inspecting and debugging MobX applications. It provides real-time inspection of the state and actions in your MobX store, similar to @xstate/inspect for XState. However, it is specifically designed for MobX and does not support XState state machines.
reactotron
Reactotron is a desktop app for inspecting and debugging React and React Native applications. It provides real-time inspection of state, actions, and network requests, similar to @xstate/inspect. However, it is more general-purpose and not specifically designed for XState state machines.
@xstate/inspect
This package contains inspection tools for XState.
Templates
Check out the XState + Vue Minute Timer + Viz example on CodeSandbox
Installation
- Install with npm or yarn:
npm install @xstate/inspect
Via CDN
<script src="https://unpkg.com/@xstate/inspect/dist/xstate-inspect.umd.min.js"></script>
By using the global variable XStateInspect
- Import it at the beginning of your project, before any other code is called:
import { inspect } from '@xstate/inspect';
inspect({
iframe: false
});
- 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 });
service.start();
Configuration
url
(optional) - The endpoint that the Inspector sends events to. Default: https://stately.ai/viz?inspectiframe
(optional) - The iframe that loads the provided URL. If iframe is set to false
, then a new tab is opened instead.devTools
(optional) - Allows custom implementation for lifecycle hooks.serialize
(optional) - A custom serializer for messages sent to the URL endpoint. Useful for sanitizing sensitive information, such as credentials, from leaving your application.targetWindow
(optional) - Provide a pre-existing window location that will be used instead of opening a new window etc. When using this option, you must still provide the url
value due to security checks in browser APIs, and the iframe
option is ignored in such a case.
Examples
Add a custom serializer to @xstate/inspector events and transitions messages
When is this useful?
- Remove sensitive items, such as
credentials
- Add additional custom handling
inspect({
serialize: (key: string, value: any) => {
return key === "credentials" && typeof value === "object" ? {} : value;
},
});
inspect({
serialize: (key: string, value: any) => {
if (key === "ready") {
console.log("Detected ready key");
}
return value;
},
});
Easily log all machine events and transitions
When is this useful?
- Allows you to quickly see all events and transitions for your machines
- No need to add manual
console.log
statements to your machine definitions
const url = "http://127.0.0.1:5174/"
const inspector = inspect({
url,
iframe: undefined,
targetWindow: window
});
createWindowReceiver({}).subscribe(console.log);
interpret(machine, { devTools: true }).start({});
Send events to a separate, locally hosted tool
When is this useful?
- Forward messages to a custom endpoint, that you can then listen to and add custom handling for messages
const url = "http://127.0.0.1:8443/"
const targetWindow = window.open(url);
const inspector = inspect({
url,
targetWindow
});
createWindowReceiver({}).subscribe((event) => {
if (event.type === "service.register") {
} else if (event.type === "service.stop") {
} else if (event.type === "service.event") {
} else if (event.type === "service.state") {
}
});