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
Top comments (0)