Skip to content

To Anyone Needing Notification Component #615

@BarishSarac

Description

@BarishSarac

It seems like our beloved Notistack is not maintained for a long time, bug correction-wise it's a problematic but worse yet when I upgraded to react v19 and this package still on the v18. I worked with AI to generate a perfect notification system that works for me, so sharing that with anyone who needs one. Modify to the way you like.

layout.tsx

  const notificationOptions = {
    maxNotifications: 5, // Show up to 5 notifications
    autoHideDuration: 5000, // Auto-hide after 5 seconds
    preventDuplicate: true, // Allow duplicate notifications
  };
  
  return(
    <NotificationProvider settings={notificationOptions}>
        <App />
    </NotificationProvider>
  )

notification-provider.tsx


import React, { createContext, useContext, useState, ReactNode } from "react";
import Notification from "./notification";

// Default configuration for the notification system
const defaultNotificationOptions = {
  maxNotifications: 3, // Maximum number of notifications to display at once
  autoHideDuration: 5000, // Auto-hide duration in milliseconds
  preventDuplicate: true, // Prevent duplicate notifications
};

interface NotificationProviderProps {
  children: ReactNode;
  settings?: Partial<typeof defaultNotificationOptions>;
}

interface Notification {
  id: string;
  message: string;
  type: "success" | "error" | "info" | "warning";
  timer?: NodeJS.Timeout; // Timer reference for auto-hide
}

interface NotificationContextType {
  addNotification: (
    message: string,
    type: "success" | "error" | "info" | "warning"
  ) => void;
  removeNotification: (id: string) => void;
  clearAllNotifications: () => void; // Clear all notifications
}

const NotificationContext = createContext<NotificationContextType | null>(null);

export const useNotifications = () => {
  const context = useContext(NotificationContext);
  if (!context) {
    throw new Error(
      "useNotifications must be used within a NotificationProvider"
    );
  }
  return context;
};

export const NotificationProvider: React.FC<NotificationProviderProps> = ({
  children,
  settings = {},
}) => {
  const notificationSettings = { ...defaultNotificationOptions, ...settings };

  const [notifications, setNotifications] = useState<Notification[]>([]);

  const addNotification = (
    message: string,
    type: "success" | "error" | "info" | "warning"
  ) => {
    setNotifications((prev) => {
      // Prevent duplicates if enabled
      if (notificationSettings.preventDuplicate) {
        const isDuplicate = prev.some(
          (n) => n.message === message && n.type === type
        );
        if (isDuplicate) return prev;
      }

      const id = Date.now().toString();

      // Create a new notification
      const newNotification: Notification = { id, message, type };

      // Add the notification and limit to maxNotifications
      const updated = [...prev, newNotification];
      if (updated.length > notificationSettings.maxNotifications) {
        const oldest = updated.shift(); // Remove the oldest notification
        if (oldest?.timer) clearTimeout(oldest.timer); // Clear its timer
      }

      // Set a timer to auto-remove the notification after the specified duration
      newNotification.timer = setTimeout(() => {
        removeNotification(id);
      }, notificationSettings.autoHideDuration);

      return updated;
    });
  };

  const removeNotification = (id: string) => {
    setNotifications((prev) => {
      const notificationToRemove = prev.find((n) => n.id === id);
      if (notificationToRemove?.timer) clearTimeout(notificationToRemove.timer); // Clear its timer
      return prev.filter((n) => n.id !== id);
    });
  };

  const clearAllNotifications = () => {
    setNotifications((prev) => {
      // Clear timers for all notifications
      prev.forEach((n) => {
        if (n.timer) clearTimeout(n.timer);
      });
      return []; // Reset the notifications array
    });
  };

  return (
    <NotificationContext.Provider
      value={{ addNotification, removeNotification, clearAllNotifications }}
    >
      {children}
      <div className="fixed bottom-5 right-5 flex flex-col items-end space-y-4 z-50">
        {notifications.map((notif) => (
          <Notification
            key={notif.id}
            message={notif.message}
            type={notif.type}
            onClose={() => removeNotification(notif.id)}
          />
        ))}
      </div>
    </NotificationContext.Provider>
  );
};

notification.tsx


interface NotificationProps {
  message: string;
  type?: "success" | "error" | "info" | "warning";
  onClose: () => void;
}

const Notification: React.FC<NotificationProps> = ({
  message,
  type = "info",
  onClose,
}) => {
  useEffect(() => {
    // No additional timer logic needed here; it's managed by the provider
    return () => {};
  }, []);

  const typeStyles = {
    success: "bg-green-500 text-white",
    error: "bg-red-500 text-white",
    info: "bg-blue-500 text-white",
    warning: "bg-yellow-500 text-black",
  };

  const icons = {
    success: "✔️", // Replace with a checkmark SVG or icon
    error: "❌", // Replace with an error SVG or icon
    info: "ℹ️", // Replace with an info SVG or icon
    warning: "⚠️", // Replace with a warning SVG or icon
  };

  return (
    <div
      className={`w-80 px-4 py-2 rounded shadow-lg flex items-center space-x-4 ${typeStyles[type]} z-50`}
    >
      <span className="text-xl">{icons[type]}</span>
      <span>{message}</span>
      <button
        className="ml-auto text-white focus:outline-none"
        onClick={onClose}
      >
        ✕
      </button>
    </div>
  );
};

export default Notification;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions