Sometimes you need simple, decoupled communication between React components — but bringing in Context Providers or Redux just for that is overkill. Here’s how you can build a tiny, global event bus in React that’s fast, flexible, and dependency-free.

Why Use a Global Event Bus?

Perfect for:

  • Triggering toast notifications from anywhere
  • Broadcasting app-wide settings changes
  • Communicating across deeply nested components

Step 1: Create a Barebones Event Emitter

This basic pub/sub system is just a few lines of vanilla JavaScript:

// eventBus.js
const listeners = new Map();

export function subscribe(event, callback) {
  if (!listeners.has(event)) {
    listeners.set(event, []);
  }
  listeners.get(event).push(callback);
  return () => {
    listeners.set(event, listeners.get(event).filter(cb => cb !== callback));
  };
}

export function publish(event, data) {
  if (listeners.has(event)) {
    listeners.get(event).forEach(callback => callback(data));
  }
}

Step 2: Create a Hook to Subscribe in React

We’ll automatically clean up subscriptions when components unmount:

// useEventBus.js
import { useEffect } from "react";
import { subscribe } from "./eventBus";

export function useEvent(eventName, handler) {
  useEffect(() => {
    const unsubscribe = subscribe(eventName, handler);
    return () => unsubscribe();
  }, [eventName, handler]);
}

Step 3: Publish Events from Anywhere

Now you can emit events from any component:

// ExamplePublisher.js
import { publish } from "./eventBus";

function ExamplePublisher() {
  return (
    
  );
}

export default ExamplePublisher;

Step 4: Listen for Events Anywhere

// ExampleSubscriber.js
import { useEvent } from "./useEventBus";

function ExampleSubscriber() {
  useEvent("toast", (data) => {
    alert(data.message);
  });

  return null;
}

export default ExampleSubscriber;

Step 5: Put It Together

// App.js
import ExamplePublisher from "./ExamplePublisher";
import ExampleSubscriber from "./ExampleSubscriber";

function App() {
  return (
    
); } export default App;

Pros and Cons

✅ Pros

  • Zero external dependencies
  • Ultra lightweight (~0.5 KB)
  • Global communication without Provider trees

⚠️ Cons

  • No built-in TypeScript types (you'd need to add those manually)
  • Harder to debug with lots of event types flying around

🚀 Alternatives

  • Zustand or Jotai if you want minimal but structured state management
  • Redux Toolkit if your app needs serious scale and devtools

Summary

If you want simple event-driven architecture in React without dragging in Contexts, Providers, or third-party libraries, a handcrafted Event Bus is ridiculously effective. Tune it, expand it, and make it fit your project’s complexity.

For a much more extensive guide on getting the most out of React portals, check out my full 24-page PDF file on Gumroad. It's available for just $10:

Using React Portals Like a Pro.

If you found this useful, you can support me here: buymeacoffee.com/hexshift