ComponentsBasic Toast
Basic Toast
A versatile toast notification system with smooth animations for different alert types including success, error, warning, and info messages.
Code
Install with shadcn Beta
Terminal
npx shadcn@latest add "https://smoothui.dev/r/basic-toast.json"
Manual install
Terminal
npm install motion lucide-react
BasicToast.tsx
"use client"
import { useEffect, useState } from "react"
import { AlertCircle, CheckCircle, Info, X, XCircle } from "lucide-react"
import { AnimatePresence, motion } from "motion/react"
export type ToastType = "success" | "error" | "info" | "warning"
export interface ToastProps {
message: string
type?: ToastType
duration?: number
onClose?: () => void
isVisible?: boolean
}
const toastIcons = {
success: <CheckCircle className="h-5 w-5 text-emerald-500" />,
error: <XCircle className="h-5 w-5 text-red-500" />,
warning: <AlertCircle className="h-5 w-5 text-amber-500" />,
info: <Info className="h-5 w-5 text-blue-500" />,
}
const toastClasses = {
success:
"border-emerald-100 bg-emerald-50 dark:border-emerald-900 dark:bg-emerald-950",
error: "border-red-100 bg-red-50 dark:border-red-900 dark:bg-red-950",
warning:
"border-amber-100 bg-amber-50 dark:border-amber-900 dark:bg-amber-950",
info: "border-blue-100 bg-blue-50 dark:border-blue-900 dark:bg-blue-950",
}
export default function BasicToast({
message,
type = "info",
duration = 3000,
onClose,
isVisible = true,
}: ToastProps) {
const [visible, setVisible] = useState(isVisible)
useEffect(() => {
setVisible(isVisible)
}, [isVisible])
useEffect(() => {
if (visible && duration > 0) {
const timer = setTimeout(() => {
setVisible(false)
onClose?.()
}, duration)
return () => clearTimeout(timer)
}
}, [visible, duration, onClose])
return (
<AnimatePresence>
{visible && (
<motion.div
className={`fixed top-4 right-4 z-50 flex w-80 items-center gap-3 rounded-lg border p-4 shadow-lg ${toastClasses[type]}`}
initial={{ opacity: 0, x: 50, scale: 0.8 }}
animate={{ opacity: 1, x: 0, scale: 1 }}
exit={{
opacity: 0,
x: 50,
scale: 0.8,
transition: { duration: 0.15 },
}}
transition={{ type: "spring", bounce: 0.25 }}
>
<div className="flex-shrink-0">{toastIcons[type]}</div>
<p className="flex-1 text-sm">{message}</p>
<button
onClick={() => {
setVisible(false)
onClose?.()
}}
className="flex-shrink-0 rounded-full p-1 transition-colors hover:bg-black/5 dark:hover:bg-white/10"
>
<X className="h-4 w-4" />
</button>
</motion.div>
)}
</AnimatePresence>
)
}
// Example of how to use this component:
export function ToastDemo() {
const [showToast, setShowToast] = useState(false)
const [toastType, setToastType] = useState<ToastType>("success")
const handleShowToast = (type: ToastType) => {
setToastType(type)
setShowToast(true)
}
return (
<div className="flex flex-col gap-4 p-4">
<div className="flex flex-wrap gap-2">
<button
onClick={() => handleShowToast("success")}
className="rounded-md bg-emerald-500 px-3 py-1.5 text-sm text-white hover:bg-emerald-600"
>
Success Toast
</button>
<button
onClick={() => handleShowToast("error")}
className="rounded-md bg-red-500 px-3 py-1.5 text-sm text-white hover:bg-red-600"
>
Error Toast
</button>
<button
onClick={() => handleShowToast("warning")}
className="rounded-md bg-amber-500 px-3 py-1.5 text-sm text-white hover:bg-amber-600"
>
Warning Toast
</button>
<button
onClick={() => handleShowToast("info")}
className="rounded-md bg-blue-500 px-3 py-1.5 text-sm text-white hover:bg-blue-600"
>
Info Toast
</button>
</div>
<AnimatePresence>
{showToast && (
<BasicToast
message={`This is a ${toastType} message example!`}
type={toastType}
duration={3000}
onClose={() => setShowToast(false)}
/>
)}
</AnimatePresence>
</div>
)
}