Toast
ComponentsA succinct message that is displayed temporarily to provide feedback about an operation.
Toast Notifications
Provides unobtrusive user feedback following an interaction. Features support for multiple notification categories (success, error, information), dismiss triggers, and animated layout reordering.
Project saved successfully
All changes synced to remote repository.
Live Functional Playground Interactive Hooks • useState
Database Synced
All schema migrations completed.
Motion Physics & Animation Showcase Framer Motion ✨
Motion Physics
✨
Project saved successfully
All changes synced to remote repository.
Hover to trigger spring elevation lift
Format:
Required Animation Library: framer-motion & lucide-react
Install this package in your project before importing the animated component.
npm i framer-motion lucide-react <div class="flex items-center justify-between w-full max-w-sm p-4 rounded-xl border border-border bg-card text-card-foreground shadow-lg">
<div class="flex items-center gap-3">
<div class="p-1 rounded-full bg-green-500/10 text-green-500">
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
</div>
<div class="space-y-0.5">
<p class="text-xs font-semibold text-foreground">Project saved successfully</p>
<p class="text-[11px] text-muted-foreground">All changes synced to remote repository.</p>
</div>
</div>
<button class="text-muted-foreground hover:text-foreground text-xs p-1">✕</button>
</div> import React from "react";
export function Toast({ title = "Saved", description = "Your data has been updated." }: { title?: string; description?: string }) {
return (
<div className="flex items-center justify-between w-full max-w-sm p-4 rounded-xl border border-border bg-card text-card-foreground shadow-lg">
<div className="space-y-0.5">
<p class="text-xs font-semibold text-foreground">{title}</p>
<p class="text-[11px] text-muted-foreground">{description}</p>
</div>
</div>
);
} "use client";
import React, { useState, useEffect } from "react";
export type ToastType = "success" | "error" | "info";
export interface ToastItem {
id: string;
type: ToastType;
title: string;
message: string;
}
export default function ToastDemo() {
const [toasts, setToasts] = useState<ToastItem[]>([
{
id: "demo-1",
type: "success",
title: "Changes Published",
message: "Your documentation edits are now live across all regions.",
},
]);
const addToast = (type: ToastType) => {
const id = Math.random().toString(36).slice(2, 9);
const item: ToastItem = {
id,
type,
title:
type === "success"
? "Build Deployed"
: type === "error"
? "Connection Timeout"
: "System Update",
message:
type === "success"
? "Static assets generated in 1.4s"
: type === "error"
? "Could not resolve API gateway"
: "Scheduled maintenance in 2 hours",
};
setToasts((prev) => [item, ...prev].slice(0, 3));
};
const removeToast = (id: string) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
};
return (
<div className="w-full max-w-md space-y-4">
{/* Toast Trigger Actions */}
<div className="flex flex-wrap items-center justify-center gap-2">
<button
type="button"
onClick={() => addToast("success")}
className="px-3 py-1.5 text-xs font-semibold rounded-lg bg-green-500/15 text-green-700 dark:text-green-300 border border-green-500/30 hover:bg-green-500/25 transition-all cursor-pointer"
>
+ Success Toast
</button>
<button
type="button"
onClick={() => addToast("error")}
className="px-3 py-1.5 text-xs font-semibold rounded-lg bg-destructive/15 text-destructive border border-destructive/30 hover:bg-destructive/25 transition-all cursor-pointer"
>
+ Error Toast
</button>
<button
type="button"
onClick={() => addToast("info")}
className="px-3 py-1.5 text-xs font-semibold rounded-lg bg-primary/15 text-primary border border-primary/30 hover:bg-primary/25 transition-all cursor-pointer"
>
+ Info Toast
</button>
</div>
{/* Rendered Toasts Container */}
<div className="space-y-2.5">
{toasts.map((toast) => (
<div
key={toast.id}
role="alert"
className="flex items-start justify-between gap-3 p-3.5 rounded-xl border border-border bg-card text-card-foreground shadow-lg animate-in fade-in slide-in-from-top-2 duration-200"
>
<div className="flex items-start gap-2.5">
<span
className={`mt-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-bold ${
toast.type === "success"
? "bg-green-500/20 text-green-600 dark:text-green-400"
: toast.type === "error"
? "bg-destructive/20 text-destructive"
: "bg-primary/20 text-primary"
}`}
>
{toast.type === "success" ? "✓" : toast.type === "error" ? "!" : "i"}
</span>
<div className="space-y-0.5">
<h5 className="text-xs font-bold text-foreground">{toast.title}</h5>
<p className="text-[11px] text-muted-foreground leading-relaxed">{toast.message}</p>
</div>
</div>
<button
type="button"
onClick={() => removeToast(toast.id)}
className="text-muted-foreground hover:text-foreground text-xs p-1 rounded-md hover:bg-muted transition-colors cursor-pointer"
>
✕
</button>
</div>
))}
{toasts.length === 0 && (
<p className="text-xs text-center text-muted-foreground py-2">
No active toasts. Click a button above to summon one.
</p>
)}
</div>
</div>
);
} "use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { CheckCircle, AlertCircle, Info, X } from "lucide-react";
export default function AnimatedToast() {
const [toasts, setToasts] = useState([
{
id: 1,
type: "success",
title: "File Upload Complete",
description: "design-system-spec.pdf (4.2 MB) saved to cloud.",
},
]);
const add = () => {
const nextId = Date.now();
setToasts((prev) => [
{
id: nextId,
type: "success",
title: "Deployment Successful",
description: `Edge function deployed to cluster #${Math.floor(Math.random() * 90 + 10)}`,
},
...prev,
]);
};
const remove = (id: number) => {
setToasts((prev) => prev.filter((t) => t.id !== id));
};
return (
<div className="w-full max-w-sm space-y-4">
<div className="flex justify-center">
<motion.button
whileHover={{ scale: 1.03 }}
whileTap={{ scale: 0.97 }}
onClick={add}
className="px-4 py-2 text-xs font-semibold rounded-xl bg-primary text-primary-foreground shadow-md hover:bg-primary/90 transition-colors"
>
Summon Toast ✨
</motion.button>
</div>
<div className="space-y-2.5">
<AnimatePresence mode="popLayout">
{toasts.map((toast) => (
<motion.div
key={toast.id}
layout
initial={{ opacity: 0, y: -20, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, scale: 0.8, transition: { duration: 0.15 } }}
transition={{ type: "spring", stiffness: 500, damping: 30 }}
className="flex items-start justify-between gap-3 p-4 rounded-2xl border border-border bg-card/90 backdrop-blur-md shadow-xl"
>
<div className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-green-500 shrink-0 mt-0.5" />
<div className="space-y-0.5">
<h4 className="text-xs font-bold text-foreground">{toast.title}</h4>
<p className="text-[11px] text-muted-foreground leading-relaxed">{toast.description}</p>
</div>
</div>
<button
type="button"
onClick={() => remove(toast.id)}
className="text-muted-foreground hover:text-foreground p-1 rounded-lg hover:bg-muted transition-colors"
>
<X className="h-3.5 w-3.5" />
</button>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
);
} Props Reference
| Prop | Type | Default | Description |
|---|---|---|---|
type | "success" | "error" | "info" | "info" | Visual state and accent color style |
title | string | "" | Primary bold summary line of the notification |
message | string | "" | Detailed descriptive notification message |
duration | number | 4000 | Auto-dismissal timeout in milliseconds |
onClose | () => void | undefined | Callback fired when dismissed |
Screen Reader & ARIA Standards
- Live Regions: Uses
role="alert"andaria-live="polite"for non-disruptive announcements to screen reader users. - Accessible Action: Close buttons feature explicit
aria-label="Dismiss notification"for clarity. - Focus Preservation: Opening a toast does not yank keyboard focus away from the active form or trigger element.