DEV Community

Cover image for How to Build a Lightweight React Global Event Bus Without Redux or Context Overhead
HexShift
HexShift

Posted on • Edited on

1

How to Build a Lightweight React Global Event Bus Without Redux or Context Overhead

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 (
    <button onClick={() => publish("toast", { message: "Hello world!" })}>
      Trigger Toast
    </button>
  );
}

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 (
    <div>
      <ExamplePublisher />
      <ExampleSubscriber />
    </div>
  );
}

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

Heroku

Amplify your impact where it matters most — building exceptional apps.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

AWS Security LIVE! Stream

Stream AWS Security LIVE!

The best security feels invisible. Learn how solutions from AWS and AWS Partners make it a reality on Security LIVE!

Learn More

👋 Kindness is contagious

If this article connected with you, consider tapping ❤️ or leaving a brief comment to share your thoughts!

Okay