Toast

Component

Premium Tailwind toast notification and Tailwind CSS snackbar alerts that bring vital information to users without disrupting workflow.

Install via CLI

Run this command to automatically add the component and its dependencies to your project.

npx @abhaysinghr516/business-wish add toast
New to the CLI? Run npx @abhaysinghr516/business-wish init first to initialize your project.

Keep users informed without interrupting their flow using a Tailwind toast notification. Also known as a Tailwind CSS snackbar, these transient alerts slide in seamlessly to confirm actions, display errors, or provide system updates, complete with built-in auto-dismissal logic.

For persistent messages that require immediate attention, use a static Alert or gracefully interrupt the user with a focused Modal.

Simple Elegant Toast

import React, { useState, useEffect } from "react";
import { X } from "lucide-react";

// Helper hook for auto-dismiss
const useAutoDismiss = (isOpen, setIsOpen, duration = 4000) => {
  useEffect(() => {
    let timeoutId;
    if (isOpen) {
      timeoutId = setTimeout(() => setIsOpen(false), duration);
    }
    return () => { if (timeoutId) clearTimeout(timeoutId); };
  }, [isOpen, setIsOpen, duration]);
};

const SimpleToast = () => {
  const [isOpen, setIsOpen] = useState(false);
  useAutoDismiss(isOpen, setIsOpen);

  return (
    <>
      <button
        onClick={() => {
          setIsOpen(false);
          setTimeout(() => setIsOpen(true), 100);
        }}
        className="px-4 py-2 bg-neutral-900 dark:bg-white text-white dark:text-neutral-900 text-[14px] font-medium rounded-xl hover:bg-neutral-800 dark:hover:bg-neutral-100 transition-colors shadow-sm"
      >
        Show Simple Toast
      </button>

      {isOpen && (
        <div className="fixed bottom-6 right-6 z-50 animate-[toast-slide-up_0.3s_cubic-bezier(0.16,1,0.3,1)]">
          <div className="flex items-center gap-3 bg-neutral-900 dark:bg-white text-white dark:text-neutral-900 px-4 py-3 rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.12)] min-w-[300px]">
            <span className="text-[14px] font-medium tracking-tight">Changes saved successfully</span>
            <button
              onClick={() => setIsOpen(false)}
              className="ml-auto p-1 text-neutral-400 hover:text-white dark:text-neutral-500 dark:hover:text-neutral-900 transition-colors rounded-full"
            >
              <X className="w-4 h-4" />
            </button>
          </div>
        </div>
      )}
      <style>{`
        @keyframes toast-slide-up {
          0% { opacity: 0; transform: translateY(20px) scale(0.95); }
          100% { opacity: 1; transform: translateY(0) scale(1); }
        }
      `}</style>
    </>
  );
};

export default SimpleToast;

Status Icon Toasts

import React, { useState, useEffect } from "react";
import { CheckCircle2, AlertCircle, X } from "lucide-react";

// Hook omitted for brevity

const StatusToast = () => {
  const [toastType, setToastType] = useState(null);
  useAutoDismiss(!!toastType, () => setToastType(null));

  return (
    <div className="flex gap-3">
      <button
        onClick={() => {
          setToastType(null);
          setTimeout(() => setToastType("success"), 100);
        }}
        className="px-4 py-2 bg-green-50 dark:bg-green-500/10 text-green-600 dark:text-green-400 text-[14px] font-medium rounded-xl hover:bg-green-100 dark:hover:bg-green-500/20 transition-colors"
      >
        Success Toast
      </button>
      <button
        onClick={() => {
          setToastType(null);
          setTimeout(() => setToastType("error"), 100);
        }}
        className="px-4 py-2 bg-red-50 dark:bg-red-500/10 text-red-600 dark:text-red-400 text-[14px] font-medium rounded-xl hover:bg-red-100 dark:hover:bg-red-500/20 transition-colors"
      >
        Error Toast
      </button>

      {toastType === "success" && (
        <div className="fixed top-6 left-1/2 -translate-x-1/2 z-50 animate-[toast-slide-down_0.3s_cubic-bezier(0.16,1,0.3,1)]">
          <div className="flex items-center gap-3 bg-white dark:bg-neutral-900 border border-neutral-200/50 dark:border-white/10 px-4 py-3 rounded-2xl shadow-xl min-w-[320px]">
            <div className="w-8 h-8 rounded-full bg-green-50 dark:bg-green-500/10 flex items-center justify-center flex-shrink-0">
              <CheckCircle2 className="w-5 h-5 text-green-500" />
            </div>
            <div className="flex flex-col">
              <span className="text-[14px] font-semibold text-neutral-900 dark:text-white">Profile Updated</span>
              <span className="text-[13px] text-neutral-500">Your information has been saved.</span>
            </div>
            <button
              onClick={() => setToastType(null)}
              className="ml-auto p-1.5 text-neutral-400 hover:text-neutral-900 dark:hover:text-white transition-colors rounded-full"
            >
              <X className="w-4 h-4" />
            </button>
          </div>
        </div>
      )}

      {toastType === "error" && (
        <div className="fixed top-6 left-1/2 -translate-x-1/2 z-50 animate-[toast-slide-down_0.3s_cubic-bezier(0.16,1,0.3,1)]">
          <div className="flex items-center gap-3 bg-white dark:bg-neutral-900 border border-neutral-200/50 dark:border-white/10 px-4 py-3 rounded-2xl shadow-xl min-w-[320px]">
             <div className="w-8 h-8 rounded-full bg-red-50 dark:bg-red-500/10 flex items-center justify-center flex-shrink-0">
              <AlertCircle className="w-5 h-5 text-red-500" />
            </div>
            <div className="flex flex-col">
              <span className="text-[14px] font-semibold text-neutral-900 dark:text-white">Connection Error</span>
              <span className="text-[13px] text-neutral-500">Failed to sync with the server.</span>
            </div>
            <button
              onClick={() => setToastType(null)}
              className="ml-auto p-1.5 text-neutral-400 hover:text-neutral-900 dark:hover:text-white transition-colors rounded-full"
            >
              <X className="w-4 h-4" />
            </button>
          </div>
        </div>
      )}
      <style>{`
        @keyframes toast-slide-down {
          0% { opacity: 0; transform: translate(-50%, -20px) scale(0.95); }
          100% { opacity: 1; transform: translate(-50%, 0) scale(1); }
        }
      `}</style>
    </div>
  );
};

export default StatusToast;

Action Toast

import React, { useState, useEffect } from "react";
import { Info, X } from "lucide-react";

// Hook omitted for brevity

const ActionToast = () => {
  const [isOpen, setIsOpen] = useState(false);
  useAutoDismiss(isOpen, setIsOpen, 6000);

  return (
    <>
      <button
        onClick={() => {
          setIsOpen(false);
          setTimeout(() => setIsOpen(true), 100);
        }}
        className="px-4 py-2 bg-neutral-100 dark:bg-neutral-800 text-neutral-900 dark:text-white text-[14px] font-medium rounded-xl hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors shadow-sm"
      >
        Archive Project
      </button>

      {isOpen && (
        <div className="fixed bottom-6 left-6 z-50 animate-[toast-slide-up_0.3s_cubic-bezier(0.16,1,0.3,1)]">
          <div className="flex items-center gap-4 bg-white dark:bg-[#0A0A0A] border border-neutral-200 dark:border-white/10 p-3 pl-4 rounded-[18px] shadow-2xl min-w-[340px]">
            <div className="w-10 h-10 rounded-full bg-blue-50 dark:bg-blue-500/10 flex items-center justify-center flex-shrink-0">
              <Info className="w-5 h-5 text-blue-500" />
            </div>
            <div className="flex flex-col mr-2">
              <span className="text-[14px] font-bold text-neutral-900 dark:text-white leading-tight">Project Archived</span>
              <span className="text-[13px] text-neutral-500">Moved to archive folder</span>
            </div>
            <div className="ml-auto flex items-center gap-2 border-l border-neutral-200/60 dark:border-white/10 pl-4">
              <button
                onClick={() => setIsOpen(false)}
                className="text-[13px] font-bold text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors"
              >
                Undo
              </button>
              <button
                onClick={() => setIsOpen(false)}
                className="p-1.5 text-neutral-400 hover:text-neutral-900 dark:hover:text-white transition-colors rounded-full"
              >
                <X className="w-4 h-4" />
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

export default ActionToast;

Promise Toast

import React, { useState } from "react";
import { CheckCircle2, Loader2 } from "lucide-react";

const PromiseToast = () => {
  const [state, setState] = useState("idle");

  const startProcess = () => {
    setState("loading");
    setTimeout(() => {
      setState("success");
      setTimeout(() => {
        setState("idle");
      }, 3000);
    }, 2000);
  };

  return (
    <>
      <button
        onClick={startProcess}
        disabled={state !== "idle"}
        className="px-4 py-2 bg-blue-600 text-white text-[14px] font-medium rounded-xl hover:bg-blue-700 transition-colors shadow-sm disabled:opacity-50"
      >
        Deploy Application
      </button>

      {state !== "idle" && (
        <div className="fixed bottom-6 right-6 z-50 animate-[toast-slide-up_0.3s_cubic-bezier(0.16,1,0.3,1)]">
          <div className="flex items-center gap-3 bg-white/70 dark:bg-neutral-900/70 backdrop-blur-xl border border-white dark:border-white/10 px-5 py-3.5 rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.12)] min-w-[280px] overflow-hidden relative">
            {state === "loading" && (
               <div className="absolute bottom-0 left-0 h-[2px] bg-blue-500 w-full animate-[progress_2s_ease-in-out]" />
            )}

            {state === "loading" ? (
              <>
                <Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
                <span className="text-[14px] font-medium text-neutral-800 dark:text-neutral-200">Deploying to production...</span>
              </>
            ) : (
              <>
                 <div className="w-5 h-5 rounded-full bg-green-500 flex items-center justify-center animate-[pop_0.3s_ease-out]">
                  <CheckCircle2 className="w-3.5 h-3.5 text-white" strokeWidth={3} />
                </div>
                <span className="text-[14px] font-medium text-neutral-900 dark:text-white animate-[fade-in_0.3s_ease-out]">Successfully deployed!</span>
              </>
            )}
          </div>
        </div>
      )}
      <style>{`
        @keyframes progress {
          0% { transform: scaleX(0); transform-origin: left; }
          100% { transform: scaleX(1); transform-origin: left; }
        }
        @keyframes pop {
          0% { transform: scale(0.5); opacity: 0; }
          70% { transform: scale(1.1); }
          100% { transform: scale(1); opacity: 1; }
        }
        @keyframes fade-in {
          0% { opacity: 0; transform: translateX(5px); }
          100% { opacity: 1; transform: translateX(0); }
        }
      `}</style>
    </>
  );
};

export default PromiseToast;