Toast
Component
Premium Tailwind toast notification and Tailwind CSS snackbar alerts that bring vital information to users without disrupting workflow.
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;