npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

broadcast-event-system

v1.0.4

Published

Type-safe event bus and broadcast service for React applications

Readme

broadcast-event-system

npm version License: MIT CI codecov

Type-safe event bus and broadcast service for React applications. Pure TypeScript implementation with zero dependencies (except React for hooks).

Features

  • 🎯 Type-safe: Full TypeScript support with generics
  • 🪶 Lightweight: No dependencies (React as peer dependency for hooks)
  • 🔄 Event Bus: In-app event communication
  • 📡 Broadcast: Cross-window/tab communication
  • ⚛️ React Hooks: Easy integration with React components
  • 🧹 Auto Cleanup: Automatic event listener cleanup on unmount

Installation

npm install broadcast-event-system
# or
yarn add broadcast-event-system
# or
pnpm add broadcast-event-system

Quick Start

Event Bus (In-App Communication)

import { useEventEmit, useEventOn } from 'broadcast-event-system';

// Define your event type
type CounterEvent = { count: number };

// Component that emits events
function CounterButton() {
  const emit = useEventEmit();

  const handleClick = () => {
    emit<CounterEvent>('counter:increment', { count: 1 });
  };

  return <button onClick={handleClick}>Increment</button>;
}

// Component that listens to events
function CounterDisplay() {
  useEventOn<CounterEvent>('counter:increment', (data) => {
    console.log('Counter incremented:', data.count);
  });

  return <div>Listening to counter events</div>;
}

Broadcast (Cross-Window Communication)

import { useBroadcast, useBroadcastOn } from 'broadcast-event-system';

// Send messages to other windows/tabs
function Sender() {
  const broadcast = useBroadcast();

  const handleClick = () => {
    broadcast('my-channel', { message: 'Hello from another tab!' });
  };

  return <button onClick={handleClick}>Send to Other Tabs</button>;
}

// Receive messages from other windows/tabs
function Receiver() {
  useBroadcastOn('my-channel', (data) => {
    console.log('Received:', data);
  });

  return <div>Listening to other tabs</div>;
}

API Reference

React Hooks

useEventEmit(service?)

Returns a function to emit events.

const emit = useEventEmit();
emit<MyEventType>('event-name', { data: 'value' });

useEventOn<T>(eventType, callback, service?)

Subscribe to an event. Automatically unsubscribes on unmount.

useEventOn<MyEventType>('event-name', (data) => {
  console.log('Received:', data);
});

useEventState<T>(eventType, initialState, service?)

Combines event subscription with state management.

type UserEvent = { name: string; age: number };

function UserProfile() {
  const user = useEventState<UserEvent>('user:updated', { name: '', age: 0 });

  return <div>{user.name} is {user.age} years old</div>;
}

useBroadcast(service?)

Returns a function to broadcast messages to other windows/tabs.

const broadcast = useBroadcast();
broadcast('channel-name', { data: 'value' });

useBroadcastOn(channelName, callback, service?)

Subscribe to broadcast messages from other windows/tabs.

useBroadcastOn('channel-name', (data) => {
  console.log('Received from another tab:', data);
});

Core Services

EventService

import { EventService, eventService } from 'broadcast-event-system/core';

// Use singleton instance
eventService.emit('event-name', { data: 'value' });
eventService.on('event-name', (payload) => console.log(payload));
eventService.off('event-name', listener);
eventService.clear('event-name');
eventService.clearAll();

// Or create your own instance
const myEvents = new EventService();

BroadcastService

import { BroadcastService, broadcastService } from 'broadcast-event-system/core';

// Use singleton instance
const listenerId = broadcastService.subscribe('channel', (data) => {
  console.log('Received:', data);
});

broadcastService.broadcast('channel', { message: 'Hello' });

// Cleanup - unsubscribe specific listener
broadcastService.unsubscribe('channel', listenerId);

// Or close entire channel (removes all listeners)
broadcastService.close('channel');

// Close all channels
broadcastService.closeAll();

// Or create your own instance
const myBroadcast = new BroadcastService();

Advanced Usage

Custom Event Service Instance

import { EventService } from 'broadcast-event-system/core';
import { useEventEmit, useEventOn } from 'broadcast-event-system';

// Create a custom instance for isolated event bus
const customEvents = new EventService();

function MyComponent() {
  const emit = useEventEmit(customEvents);

  useEventOn('my-event', (data) => {
    console.log(data);
  }, customEvents);

  // This event is isolated from the global event bus
  return <button onClick={() => emit('my-event', { test: true })}>Click</button>;
}

Event Naming Conventions

We recommend using namespaced event names:

// Good
'user:login'
'user:logout'
'cart:add-item'
'cart:remove-item'
'notification:show'

// Avoid
'login'
'addItem'
'show'

TypeScript Best Practices

Define event types for better type safety:

// events.types.ts
export type UserLoginEvent = {
  userId: string;
  timestamp: number;
};

export type CartAddItemEvent = {
  itemId: string;
  quantity: number;
};

// In your component
import { useEventEmit } from 'broadcast-event-system';
import type { UserLoginEvent } from './events.types';

const emit = useEventEmit();
emit<UserLoginEvent>('user:login', {
  userId: '123',
  timestamp: Date.now(),
});

Real-World Example

// EventTypes.ts
export type NotificationEvent = {
  type: 'success' | 'error' | 'info';
  message: string;
  duration?: number;
};

// NotificationManager.tsx
import { useEventOn } from 'broadcast-event-system';
import { NotificationEvent } from './EventTypes';

export function NotificationManager() {
  const [notifications, setNotifications] = useState<NotificationEvent[]>([]);

  useEventOn<NotificationEvent>('notification:show', (data) => {
    setNotifications((prev) => [...prev, data]);

    setTimeout(() => {
      setNotifications((prev) => prev.filter((n) => n !== data));
    }, data.duration || 3000);
  });

  return (
    <div className="notifications">
      {notifications.map((notif, idx) => (
        <div key={idx} className={`notification ${notif.type}`}>
          {notif.message}
        </div>
      ))}
    </div>
  );
}

// Anywhere in your app
import { useEventEmit } from 'broadcast-event-system';
import { NotificationEvent } from './EventTypes';

function MyForm() {
  const emit = useEventEmit();

  const handleSubmit = async () => {
    try {
      await saveData();
      emit<NotificationEvent>('notification:show', {
        type: 'success',
        message: 'Data saved successfully!',
      });
    } catch (error) {
      emit<NotificationEvent>('notification:show', {
        type: 'error',
        message: 'Failed to save data',
      });
    }
  };

  return <button onClick={handleSubmit}>Save</button>;
}

Testing

This package includes comprehensive test coverage using Vitest and React Testing Library.

Running Tests

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage
npm run test:coverage

# Open Vitest UI
npm run test:ui

Test Coverage

The project maintains high test coverage:

  • EventService: 100% coverage
  • BroadcastService: 100% coverage
  • React Hooks: 100% coverage
    • useEventEmit
    • useEventOn
    • useEventState
    • useBroadcast
    • useBroadcastOn

Total: 122 tests across 6 test suites

Coverage reports are generated in the coverage/ directory. Open coverage/index.html in your browser to view detailed coverage information.

CI/CD

This project uses GitHub Actions for continuous integration:

  • Automated Testing: Tests run on Node.js 18, 20, and 22
  • Build Verification: Ensures TypeScript compilation succeeds
  • Coverage Reporting: Automatic upload to Codecov

See .github/workflows/ci.yml for the complete CI configuration.

Changelog

See CHANGELOG.md for version history and release notes.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.