Sharing & Tooling
Sharing a machine across components
useSharedMachine(config, options?) creates or retrieves a singleton machine instance by config.id. Useful when unrelated components need to share the same running machine without prop-drilling or Pinia.
function useSharedMachine<TState, TEvent, TContext>(
config: MachineConfig<TState, TEvent, TContext>,
options?: UseMachineOptions,
): MachineInstance<TState, TEvent, TContext>Requires VueMachinePlugin to be installed.
// In component A
const { state } = useSharedMachine(cartMachine)
// In component B (completely separate tree)
const { send } = useSharedMachine(cartMachine)
// Both share the same machine instance — same state, same context
await send('ADD_ITEM') // component A's state.value updates reactivelyIf a machine with config.id is already registered in the store, the existing instance is returned (and internally, the store's reference count for that id is bumped — see Accessing the store directly). Otherwise a new one is created and registered automatically.
Note: only the first
useSharedMachine(config, options)call for a given id applies itsoptions(includingpersist) — every later call anywhere else in the tree gets whatever the first caller configured, regardless of what it itself passes. In development, if a later call'soptionsmeaningfully differ from what the shared instance was actually created with (historyLimit, orpersist.key/persist.storage), aconsole.warnis logged so the mismatch doesn't go unnoticed — but the later call's ownoptionsare still silently ignored either way.
Vue plugin
Install VueMachinePlugin to enable the global machine registry (useMachineStore, useSharedMachine) and DevTools integration.
import { createApp } from 'vue'
import { VueMachinePlugin } from '@macrulez/vue-state-machine'
import App from './App.vue'
const app = createApp(App)
app.use(VueMachinePlugin)
app.mount('#app')Accessing the store directly
useMachineStore() provides direct access to the global registry. Useful for debugging or admin UIs.
const store = useMachineStore()
store.register('cart', instance) // register manually (optionally: register('cart', instance, options))
store.retain('cart') // mark another user of an already-registered id, without replacing it
store.unregister('cart') // release one use; the entry is only actually removed once every register()/retain() has a matching unregister()
store.get('cart') // MachineInstance | undefined
store.getOptions('cart') // the UseMachineOptions the current instance was actually created with, or undefined
store.getAll() // Map<string, MachineInstance>Calling useMachineStore() without the plugin installed throws a descriptive error.
The store is reference-counted: register() and retain() each increment an internal count for the id, and unregister() decrements it, only actually removing the entry once the count reaches zero. useMachine() (and, transitively, useWizard(), which is built on it) automatically calls store.unregister(id) in onUnmounted, and useSharedMachine() does the same for every caller that reuses an existing entry via retain() — so a machine shared between several components via the same id survives until every sharer has unmounted, not just the one that happened to create it first. If you call store.register() yourself outside of these composables, you're responsible for calling store.unregister() yourself too, the same way.
Note: in development,
store.register(id, ...)logs aconsole.warnifidis already registered — this usually means twodefineMachine()/useMachine()calls (or an explicitWizardOptions.id) are using the same id unintentionally. The prior instance is still overwritten either way; the warning is visibility only.
DevTools
The DevTools integration lives in a separate entry point so it never ends up in production bundles.
import { createApp } from 'vue'
import { VueMachinePlugin } from '@macrulez/vue-state-machine'
import { VueMachineDevtools } from '@macrulez/vue-state-machine/devtools'
import App from './App.vue'
const app = createApp(App)
app.use(VueMachinePlugin)
// Only in development
if (import.meta.env.DEV) {
app.use(VueMachineDevtools)
}
app.mount('#app')Panel features:
- Registers a "State Machines" settings panel in Vue DevTools
- On the DevTools inspector's
visitComponentTreerefresh, emits one timeline event per registered machine (fromMachineStore) with its current state and context — this is a live snapshot taken each time DevTools inspects the tree, not a push on every individualsend(); a transition that happens between two inspections isn't captured on its own
VueMachinePluginmust be installed beforeVueMachineDevtools— it looks up the store viaapp._context.providesand warns (not throws) if the plugin isn't installed yet.