DEV Community

JAKER HOSSAIN
JAKER HOSSAIN

Posted on

Create Reusable Toaster Notifications in React with Custom Animations

Learn how to build a reusable toaster component in React that supports different notification types like success, error, and warning. It includes features like smooth fade-out animations, progress bars, and customizable positions. This is a quick and effective way to improve user experience in your web applications by providing non-intrusive notifications.

📢 Key Features:

🎉 Customizable Notification Types: Choose from success, error, warning, and info.
🚀 Smooth Animations: Includes slide-in and fade-out animations.
📍 Position Control: Place the notification at any corner of the screen.
⏳ Progress Bar: Visualize the time remaining before the notification disappears.
⏲️ Adjustable Duration: Set how long the notification stays visible.

"use client";
import { IconX } from "@tabler/icons-react";
import React, { useEffect, useState } from "react";
import styles from "./Toaster.module.css";

const Toaster = ({
  message,
  duration = 3,
  onClose,
  type = "success",
  position = "top-right",
  toasterStyle,
  progressBarStyle,
}: {
  message: string;
  duration?: number;
  onClose?: () => void;
  type?: "success" | "error" | "warning" | "info";
  position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";
  toasterStyle?: string;
  progressBarStyle?: string;
}) => {
  const [isVisible, setIsVisible] = useState(false);
  const [isFading, setIsFading] = useState(false);

  useEffect(() => {
    setIsVisible(true);

    const fadeTimer = setTimeout(() => {
      setIsFading(true);
    }, (duration - 0.5) * 1000);

    const closeTimer = setTimeout(() => {
      setIsVisible(false);
      if (onClose) onClose();
    }, duration * 1000);

    return () => {
      clearTimeout(fadeTimer);
      clearTimeout(closeTimer);
    };
  }, [duration, onClose]);

  const handleClose = () => {
    setIsFading(true);
    setTimeout(() => {
      setIsVisible(false);
      if (onClose) onClose();
    }, 500);
  };

  if (!isVisible) return null;

  return (
    <div
      className={`${styles.toaster} ${styles[type]} ${styles[position]} ${
        isFading ? styles["fade-out"] : ""
      } fixed rounded-md px-3 py-2 flex items-center justify-between gap-x-2  ${toasterStyle}`}
    >
      <span className="text-xs md:text-sm">{message}</span>
      <span
        onClick={handleClose}
        className="cursor-pointer transition-opacity ease-in hover:opacity-70"
      >
        <IconX className="w-4 h-4" />
      </span>
      <div
        className={`${styles["progress-bar"]} ${progressBarStyle}`}
        style={{ animationDuration: `${duration - 0.5}s` }}
      ></div>
    </div>
  );
};

export default Toaster;

Enter fullscreen mode Exit fullscreen mode
.toaster {
  z-index: 1000;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  transform: translateX(150%);
  animation: slideIn 0.5s ease-out forwards;
  position: fixed;
  min-width: 250px;
  max-width: 400px;
  padding: 12px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 10px;
}

.fade-out {
  animation: fadeOut 0.5s ease-in forwards;
}

.top-right {
  top: 10px;
  right: 10px;
}
.top-left {
  top: 10px;
  left: 10px;
}
.bottom-right {
  bottom: 10px;
  right: 10px;
}
.bottom-left {
  bottom: 10px;
  left: 10px;
}

.success {
  background-color: #22c55e;
  color: white;
}
.error {
  background-color: #ef4444;
  color: white;
}
.warning {
  background-color: #f59e0b;
  color: white;
}
.info {
  background-color: #3b82f6;
  color: white;
}

.progress-bar {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  background-color: white;
  animation: progress linear forwards;
}

@keyframes slideIn {
  from {
    transform: translateX(150%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

@keyframes progress {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}

Enter fullscreen mode Exit fullscreen mode

Toaster notifications are a great way to keep users informed without interrupting their experience. By building a customizable toaster system in React, you can easily adapt it to fit your app's design and user needs. With features like smooth animations, customizable positions, progress bars, and adjustable durations, this simple component can significantly enhance your web application's user experience. Try implementing it in your next project for non-intrusive, yet effective notifications.

Neon image

Serverless Postgres in 300ms (❗️)

10 free databases with autoscaling, scale-to-zero, and read replicas. Start building without infrastructure headaches. No credit card needed.

Try for Free →

Top comments (0)

AWS GenAI LIVE!

GenAI LIVE! is a dynamic live-streamed show exploring how AWS and our partners are helping organizations unlock real value with generative AI.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️