Skip to main content

Overview

The editor provides a typed event bus for communication between components. It’s a singleton instance built on the browser’s native CustomEvent API with events prefixed as @react-email/editor:.
import { editorEventBus } from '@react-email/editor/core';

Dispatching events

Fire an event with a payload:
editorEventBus.dispatch('bubble-menu:add-link', undefined);

Listening to events

Subscribe to events and clean up when done:
import { useEffect } from 'react';
import { editorEventBus } from '@react-email/editor/core';

function MyComponent() {
  useEffect(() => {
    const subscription = editorEventBus.on('bubble-menu:add-link', () => {
      console.log('Link addition triggered');
    });

    return () => {
      subscription.unsubscribe();
    };
  }, []);

  return null;
}
The on method returns an object with an unsubscribe function. Always unsubscribe in a cleanup function to avoid memory leaks.

Built-in events

EventPayloadDescription
bubble-menu:add-linkundefinedTriggered when the “add link” action is initiated from the bubble menu

Adding custom events

Use TypeScript module augmentation to register custom events with full type safety:
declare module '@react-email/editor/core' {
  interface EditorEventMap {
    'my-feature:custom-event': { data: string };
    'my-feature:another-event': { count: number };
  }
}
Then dispatch and listen with full type checking:
// TypeScript knows the payload type
editorEventBus.dispatch('my-feature:custom-event', { data: 'hello' });

editorEventBus.on('my-feature:custom-event', (payload) => {
  // payload is typed as { data: string }
  console.log(payload.data);
});

Event targets

By default, events are dispatched on window. You can scope events to a specific DOM element using the target option:
// Dispatch on a specific element
const container = document.getElementById('my-editor');
editorEventBus.dispatch('bubble-menu:add-link', undefined, {
  target: container,
});

// Listen on a specific element
editorEventBus.on('bubble-menu:add-link', handler, {
  target: container,
});
This is useful when you have multiple editor instances on the same page and want events to stay scoped to their respective editors.