Skip to content
Version: XState v5

Event emitter

Since XState version 5.9.0

State machines and other types of actor logic in XState have the ability to emit events. This allows external event handlers to be notified of specific events.

With state machines, you can emit events using the emit(event) action creator.

import { setup, emit } from 'xstate';

const machine = setup({
actions: {
emitEvent: emit({ type: 'notification' }),
},
}).createMachine({
// ...
on: {
someEvent: {
actions: { type: 'emitEvent' },
},
},
});

const actor = createActor(machine);

actor.on('notification', (event) => {
console.log('Notification received!', event);
});

actor.start();

actor.send({ type: 'someEvent' });
// Logs:
// "Notification received!"
// { type: "notification" }

Emitting events from actor logic​

For promise actors, transition actors, observable actors, and callback actors, you can use the emit method from the arguments to emit events.

Promise actors

import { fromPromise } from 'xstate';

const logic = fromPromise(async ({ emit }) => {
// ...
emit({
type: 'emitted',
msg: 'hello',
});
// ...
});

Transition actors

import { fromTransition } from 'xstate';

const logic = fromTransition((state, event, { emit }) => {
// ...
emit({
type: 'emitted',
msg: 'hello',
});
// ...
return state;
}, {});

Observable actors

import { fromObservable } from 'xstate';

const logic = fromObservable(({ emit }) => {
// ...
emit({
type: 'emitted',
msg: 'hello',
});
// ...
});

Callback actors

import { fromCallback } from 'xstate';

const logic = fromCallback(({ emit }) => {
// ...
emit({
type: 'emitted',
msg: 'hello',
});
// ...
});

Emit action creator​

The emit action is a special action that emits an event to any external event handlers from state machine logic. The emitted event can be statically or dynamically defined:

import { setup, emit } from 'xstate';

const machine = setup({
actions: {
// Emitting a statically-defined event
emitStaticEvent: emit({
type: 'someStaticEvent',
data: 42,
}),

// Emitting a dynamically-defined event based on context
emitDynamicEvent: emit(({ context }) => ({
type: 'someDynamicEvent',
data: context.someData,
})),
},
}).createMachine({
// ...
on: {
someEvent: {
actions: [{ type: 'emitStaticEvent' }, { type: 'emitDynamicEvent' }],
},
},
});

Event handlers​

You can attach event handlers to the actor to listen for emitted events by using actor.on(event, handler):

const someActor = createActor(someMachine);

someActor.on('someEvent', (emittedEvent) => {
// Handle the emitted event
console.log(emittedEvent);
});

someActor.start();

The actor.on(…) method returns a subscription object. You can call .unsubscribe() on it to remove the handler:

const someActor = createActor(someMachine);

const subscription = someActor.on('someEvent', (emittedEvent) => {
// Handle the emitted event
console.log(emittedEvent);
});

someActor.start();

// ...

// Stop listening for events
subscription.unsubscribe();

Wildcard event handlers​

You can listen for any emitted event by listening for the wildcard '*':

const someActor = createActor(someMachine);

actor.on('*', (emitted) => {
console.log(emitted); // Any emitted event
});

The emitted event will be typed as the union of all possible events that can be emitted from the machine.

TypeScript​

You can strongly type emitted events by defining the emitted event types in the types.emitted property of the setup(…) function:

import { setup, emit, createActor } from 'xstate';

const machine = setup({
types: {
emitted: {} as
| { type: 'notification'; message: string }
| { type: 'error'; error: Error },
// ...
},
}).createMachine({
// ...
on: {
someEvent: {
actions: [
// Strongly typed emitted event
emit({ type: 'notification', message: 'Hello' }),
],
},
},
});

const actor = createActor(machine);

// Strongly typed event handler
actor.on('notification', (event) => {
console.log(event.message); // string
});