Web Loom logoWeb.loom
Published PackagesEvent Bus Core

Event Bus Core

A lightweight, framework-agnostic Event Bus library for decoupled communication in modern web applications.

Event Bus Core

@web-loom/event-bus-core is a lightweight, framework-agnostic Event Bus library for decoupled communication in modern web applications. It provides a simple and efficient publish-subscribe pattern implementation written in TypeScript.

Features

  • Type-safe: Full TypeScript support with type inference for event names and payloads
  • Framework-agnostic: Works with React, Vue, Angular, Svelte, or vanilla JavaScript
  • Lightweight: Minimal bundle size (~1KB gzipped) with zero dependencies
  • Simple API: Intuitive methods for event registration, emission, and cleanup
  • Flexible: Support for single and multiple event listeners
  • Reliable: Stable listener execution order and memory leak prevention

Installation

npm install @web-loom/event-bus-core

Quick Start

import { createEventBus } from '@web-loom/event-bus-core';
 
// Create an event bus
const eventBus = createEventBus();
 
// Subscribe to an event
eventBus.on('user:login', (user) => {
  console.log('User logged in:', user);
});
 
// Emit an event
eventBus.emit('user:login', { id: '123', name: 'Alice' });

Type-Safe Usage

Define an EventMap interface to get full type safety and autocomplete:

import { createEventBus, type EventMap } from '@web-loom/event-bus-core';
 
// Define your application's events
interface AppEvents extends EventMap {
  'user:login': [{ userId: string; username: string }];
  'user:logout': [];
  'notification:show': [{ message: string; type: 'info' | 'error' | 'success' }];
  'data:updated': [{ entityId: string; changes: Record<string, any> }];
}
 
// Create a type-safe event bus
const eventBus = createEventBus<AppEvents>();
 
// TypeScript will enforce correct event names and payload types
eventBus.on('user:login', (payload) => {
  // payload is typed as { userId: string; username: string }
  console.log(`User ${payload.username} logged in`);
});
 
eventBus.emit('user:login', { userId: '123', username: 'Alice' });

API Reference

createEventBus<M>()

Creates a new event bus instance.

const eventBus = createEventBus<MyEventMap>();

on(event, listener)

Registers a listener function for one or more events.

// Single event
eventBus.on('user:login', (user) => {
  console.log('User logged in:', user);
});
 
// Multiple events with the same handler
eventBus.on(['user:login', 'user:register'], (user) => {
  console.log('User event:', user);
});

once(event, listener)

Registers a listener that executes only once, then automatically unsubscribes.

eventBus.once('app:ready', () => {
  console.log('App is ready! This will only log once.');
});
 
eventBus.emit('app:ready');
eventBus.emit('app:ready'); // Listener won't fire again

emit(event, ...args)

Emits an event, calling all registered listeners with the provided arguments.

eventBus.emit('notification:show', {
  message: 'Operation successful',
  type: 'success',
});

off(event?, listener?)

Unregisters listeners. Three usage patterns:

// Remove a specific listener from an event
const handler = (data) => console.log(data);
eventBus.on('user:login', handler);
eventBus.off('user:login', handler);
 
// Remove all listeners for an event
eventBus.off('user:login');
 
// Remove all listeners from all events
eventBus.off();

Practical use cases

  1. Cross-component coordination: Share a singleton bus (e.g., globalEventBus) between unrelated UI fragments, such as a navigation shell and a notification toaster. Emit notification:show from business logic, and let a toast queue behavior attach listeners without adding props or context.
  2. Commands and behaviors: Packages like @web-loom/ui-patterns instantiate local buses to coordinate focus, flow, and layout state without referencing DOM siblings directly.
  3. Event aggregation: Group multiple lower-level events into a single orders:changed signal so consumers can refresh views once per cycle instead of reacting to every CRUD event.
  4. Filtered listeners: Create helper factories that wrap eventBus.on but only forward payloads that match a filter (user ID, resource type).
  5. Global lifecycle hooks: Signal app initialization (app:ready), environment changes (theme:changed), or cleanup (app:shutdown).

Where it's used

  • apps/task-flow-ui and apps/mvvm-react-integrated create shared providers or services that emit lifecycle, notification, and selection events without tightly coupling components.
  • @web-loom/ui-patterns behaviors such as hub-and-spoke, modal, toast-queue, and sidebar-shell spin up their own buses to notify siblings about state changes (selection, open/close, status updates) while staying renderer-agnostic.
Was this helpful?
Web Loom logoWeb.loom
Copyright © Web Loom. All rights reserved.