Skip to content

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.

ts
function useSharedMachine<TState, TEvent, TContext>(
  config: MachineConfig<TState, TEvent, TContext>,
  options?: UseMachineOptions,
): MachineInstance<TState, TEvent, TContext>

Requires VueMachinePlugin to be installed.

ts
// 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 reactively

If 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 its options (including persist) — 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's options meaningfully differ from what the shared instance was actually created with (historyLimit, or persist.key/persist.storage), a console.warn is logged so the mismatch doesn't go unnoticed — but the later call's own options are still silently ignored either way.

Vue plugin ​

Install VueMachinePlugin to enable the global machine registry (useMachineStore, useSharedMachine) and DevTools integration.

ts
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.

ts
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 a console.warn if id is already registered — this usually means two defineMachine()/useMachine() calls (or an explicit WizardOptions.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.

ts
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 visitComponentTree refresh, emits one timeline event per registered machine (from MachineStore) with its current state and context — this is a live snapshot taken each time DevTools inspects the tree, not a push on every individual send(); a transition that happens between two inspections isn't captured on its own

VueMachinePlugin must be installed before VueMachineDevtools — it looks up the store via app._context.provides and warns (not throws) if the plugin isn't installed yet.