Custom Events and the EventTarget interface let different parts of a WordPress plugin communicate without tight coupling — no global variables, no jQuery triggers, just the native browser event system.
Problem: Plugin components need to communicate with each other — for example, a cart update triggering a sidebar refresh — but direct function calls create tight coupling and make components hard to test or replace independently.
Solution: Use the browser's native CustomEvent API to dispatch and listen for domain-specific events on document or a shared element. This decouples the sender from the receiver: any number of listeners can react to the same event without the dispatcher knowing they exist.
The examples below dispatch a typed CustomEvent from a block editor plugin, listen for it in a separate sidebar panel, and show how to create a standalone event bus using a bare EventTarget instance.
// dispatch-event.js — fired inside a Gutenberg block's edit() function
function notifyCartUpdate( productId, qty ) {
const event = new CustomEvent( 'myplugin:cartUpdate', {
bubbles: true, // propagates up the DOM tree
composed: true, // crosses shadow DOM boundaries
detail: { productId, qty, timestamp: Date.now() },
} );
document.dispatchEvent( event );
}
// listen-event.js — loaded by a separate sidebar panel script
document.addEventListener( 'myplugin:cartUpdate', ( e ) => {
const { productId, qty } = e.detail;
console.log( `Product ${ productId } qty changed to ${ qty }` );
updateSidebarBadge( qty );
} );
// Always namespace your events to avoid collisions with WP core or other plugins
// BAD: new CustomEvent('update')
// GOOD: new CustomEvent('myplugin:cartUpdate')
Create a reusable event bus with a standalone EventTarget — no DOM required:
// event-bus.js — shared singleton module
export const bus = new EventTarget();
// publisher.js
import { bus } from './event-bus.js';
export function publishSaveComplete( postId ) {
bus.dispatchEvent( new CustomEvent( 'saveComplete', { detail: { postId } } ) );
}
// subscriber.js
import { bus } from './event-bus.js';
bus.addEventListener( 'saveComplete', ( e ) => {
showNotification( `Post ${ e.detail.postId } saved!` );
} );
// Remove listener when the component unmounts (important in React/Gutenberg):
function handleSave( e ) { /* ... */ }
bus.addEventListener( 'saveComplete', handleSave );
// cleanup:
bus.removeEventListener( 'saveComplete', handleSave );
NOTE: Prefer CustomEvent over jQuery's $.trigger() in new code — it works in all modern browsers, requires no library, and integrates naturally with wp.hooks and the block editor's data layer.