Overview
The useActor composition function creates an actor from XState logic and manages its lifecycle within a Vue component.
function useActor < TLogic extends AnyActorLogic >(
logic : TLogic ,
options ?: ActorOptions < TLogic >
) : {
snapshot : Ref < SnapshotFrom < TLogic >>;
send : Actor < TLogic >[ 'send' ];
actorRef : Actor < TLogic >;
}
Basic Usage
State Machine
Promise Logic
Transition Logic
< script setup >
import { useActor } from '@xstate/vue' ;
import { createMachine } from 'xstate' ;
const toggleMachine = createMachine ({
id: 'toggle' ,
initial: 'inactive' ,
states: {
inactive: {
on: { TOGGLE: 'active' }
},
active: {
on: { TOGGLE: 'inactive' }
}
}
});
const { snapshot , send } = useActor ( toggleMachine );
</ script >
< template >
< div >
< p > State: {{ snapshot . value }} </ p >
< button @ click = " send ({ type: 'TOGGLE' }) " > Toggle </ button >
</ div >
</ template >
With Options
Pass options to configure the actor:
< script setup >
import { useActor } from '@xstate/vue' ;
import { createMachine } from 'xstate' ;
const machine = createMachine ({
context : ({ input }) => ({
userId: input . userId ,
data: null
}),
// ...
});
const { snapshot , send } = useActor ( machine , {
input: { userId: '123' },
inspect : ( event ) => {
console . log ( 'Inspector:' , event );
}
});
</ script >
Accessing the Actor Ref
The actorRef allows direct interaction with the underlying actor:
< script setup >
import { useActor } from '@xstate/vue' ;
import { onMounted , onUnmounted } from 'vue' ;
import { myMachine } from './myMachine' ;
const { snapshot , send , actorRef } = useActor ( myMachine );
// Subscribe to state changes
let subscription ;
onMounted (() => {
subscription = actorRef . subscribe (( state ) => {
console . log ( 'State changed:' , state );
});
});
onUnmounted (() => {
subscription ?. unsubscribe ();
});
// Get current snapshot at any time
const currentState = actorRef . getSnapshot ();
</ script >
Reactive Snapshot
The snapshot ref automatically updates when the actor’s state changes:
< script setup >
import { useActor } from '@xstate/vue' ;
import { computed } from 'vue' ;
import { fetchMachine } from './fetchMachine' ;
const { snapshot , send } = useActor ( fetchMachine );
// Computed properties based on snapshot
const isLoading = computed (() => snapshot . value . matches ( 'loading' ));
const data = computed (() => snapshot . value . context . data );
const hasError = computed (() => snapshot . value . matches ( 'error' ));
</ script >
< template >
< div >
< div v-if = " isLoading " > Loading... </ div >
< div v-else-if = " hasError " > Error occurred </ div >
< div v-else > {{ data }} </ div >
</ div >
</ template >
useMachine Alias
The useMachine function is an alias for useActor with the same API:
< script setup >
import { useMachine } from '@xstate/vue' ;
import { createMachine } from 'xstate' ;
const machine = createMachine ({
// ...
});
// useMachine and useActor are identical
const { snapshot , send , actorRef } = useMachine ( machine );
</ script >
Lifecycle Management
The actor lifecycle is automatically managed:
Component Mount (onMounted)
Observer/listener subscriptions are created (if provided)
Actor is started via actorRef.start()
Component Unmount (onBeforeUnmount)
Actor is stopped via actorRef.stop()
Subscriptions are cleaned up
< script setup >
import { useActor } from '@xstate/vue' ;
import { myMachine } from './myMachine' ;
// Actor automatically starts on mount
const { snapshot , send } = useActor ( myMachine );
// Actor automatically stops on unmount
</ script >
Error Handling
The useActor hook expects actor logic, not an existing actor ref. If you pass an actor ref, it will throw an error in development:
< script setup >
import { useActor } from '@xstate/vue' ;
import { createActor , createMachine } from 'xstate' ;
const machine = createMachine ({ /* ... */ });
const actorRef = createActor ( machine );
// ❌ This will throw an error
const { snapshot } = useActor ( actorRef );
// ✅ Use useSelector instead for existing actors
import { useSelector } from '@xstate/vue' ;
const snapshot = useSelector ( actorRef , ( s ) => s );
</ script >
TypeScript
The hook is fully typed:
import { setup } from 'xstate' ;
import { useActor } from '@xstate/vue' ;
const machine = setup ({
types: {
context: {} as { count : number ; name : string },
events: {} as
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'SET_NAME' ; name : string }
}
}). createMachine ({
context: { count: 0 , name: '' },
// ...
});
const { snapshot , send , actorRef } = useActor ( machine );
// All types are inferred
snapshot . value . context . count ; // number
snapshot . value . context . name ; // string
// TypeScript validates events
send ({ type: 'INCREMENT' }); // ✅
send ({ type: 'SET_NAME' , name: 'Alice' }); // ✅
send ({ type: 'INVALID' }); // ❌ TypeScript error
useActorRef Get actor ref without reactive snapshot
useSelector Select derived values from snapshots