πŸš€ Building a Notification System in React Using the Pub-Sub Pattern



This content originally appeared on DEV Community and was authored by Prasanna Vijayan

Say Goodbye to Prop Drilling & Context! πŸŽ‰

Introduction
Every React app needs notificationsβ€”whether it’s a success message, an error alert, or a warning toast. But how do we manage notifications globally without Redux, Context, or prop drilling?

The answer: The Publish-Subscribe (Pub-Sub) Pattern!

In this post, we’ll build a notification system that:
βœ… Uses Pub-Sub instead of Context or Redux
βœ… Works globally without prop drilling
βœ… Automatically dismisses notifications
βœ… Stacks notifications on top of each other

🧠 What is the Pub-Sub Pattern?
The Publish-Subscribe (Pub-Sub) Pattern is a messaging system where:

Publishers send out events.
Subscribers listen for those events and react accordingly.
This makes it perfect for notifications because any part of the app can trigger an event, and the notification system will handle it automaticallyβ€”without requiring direct dependencies!

πŸ›  Step 1: Create an Event Bus (Pub-Sub Utility)
Since JavaScript lacks a built-in Pub-Sub system, we’ll create a lightweight event bus.

πŸ”Ή Create a new file: eventBus.js

const eventBus = {
  events: {},

  subscribe(eventName, callback) {
    if (!this.events[eventName]) {
      this.events[eventName] = [];
    }
    this.events[eventName].push(callback);

    // Return unsubscribe function
    return () => {
      this.events[eventName] = this.events[eventName].filter(fn => fn !== callback);
    };
  },

  publish(eventName, data) {
    if (this.events[eventName]) {
      this.events[eventName].forEach(callback => callback(data));
    }
  }
};

export default eventBus;

πŸ” How This Works:
βœ… subscribe(eventName, callback): Adds a listener for an event.
βœ… publish(eventName, data): Triggers all callbacks listening to an event.
βœ… Returns an unsubscribe function so we can clean up listeners when needed.

πŸ“Œ Step 2: Create the Notification List Component
The NotificationList will:
βœ… Listen for notify events from the eventBus
βœ… Store notifications in local state
βœ… Automatically remove notifications after 3 seconds

πŸ”Ή Create a new file: NotificationList.js

import React, { useState, useEffect } from "react";
import eventBus from "./eventBus";
import "./Notifications.css";

const NotificationList = () => {
  const [notifications, setNotifications] = useState([]);

  useEffect(() => {
    // Subscribe to "notify" event
    const unsubscribe = eventBus.subscribe("notify", (notification) => {
      const id = Date.now();
      setNotifications((prev) => [...prev, { id, ...notification }]);

      // Auto-remove notification after 3 seconds
      setTimeout(() => {
        setNotifications((prev) => prev.filter((n) => n.id !== id));
      }, 3000);
    });

    return () => unsubscribe(); // Cleanup on unmount
  }, []);

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

export default NotificationList;

🎨 Step 3: Add Some Styles for Notifications
πŸ”Ή Create a new file: Notifications.css

.notifications-container {
  position: fixed;
  top: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 1000;
}

.notification {
  padding: 10px 15px;
  color: white;
  font-weight: bold;
  border-radius: 5px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  opacity: 0.9;
  animation: fade-in 0.3s ease-in-out;
}

.notification.success {
  background-color: green;
}

.notification.failure {
  background-color: red;
}

.notification.warning {
  background-color: orange;
}

@keyframes fade-in {
  from {
    transform: translateY(-10px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

⚑ Step 4: Use It in the App Component
Now, let’s wire it all together by publishing notifications when buttons are clicked.

πŸ”Ή Modify App.js

import React from "react";
import eventBus from "./eventBus";
import NotificationList from "./NotificationList";

const App = () => {
  const notify = (type, message) => {
    eventBus.publish("notify", { type, message });
  };

  return (
    <div>
      <h2>Notification System (Pub-Sub Pattern)</h2>
      <button onClick={() => notify("success", "Success!")}>Success</button>
      <button onClick={() => notify("failure", "Failure!")}>Failure</button>
      <button onClick={() => notify("warning", "Warning!")}>Warning</button>

      <NotificationList />
    </div>
  );
};

export default App;

🎯 How Everything Works Together

1⃣ Click a button β†’ eventBus.publish(“notify”, { type, message })
2⃣ NotificationList listens for “notify” events
3⃣ Notification appears in the UI
4⃣ After 3 seconds, the notification auto-disappears

πŸ“ Final Thoughts: Why Use Pub-Sub?

Using Pub-Sub in React is powerful because:
βœ… No Context or Redux needed β†’ Clean & lightweight
βœ… Fully decoupled β†’ Components don’t need to know about each other
βœ… Scalable β†’ Can be extended to support WebSockets, logging, and more

πŸ”₯ Next Steps

Want to take this further? Try adding:
βœ… A close button to manually remove notifications
βœ… Animated entry/exit effects
βœ… Persistent notifications that stay until dismissed

πŸš€ Now go and implement this in your projects! Happy coding! πŸŽ‰


This content originally appeared on DEV Community and was authored by Prasanna Vijayan