Dismissible Alert
Original · freeDismissible alerts with a working close button.
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/alert-dismissible.json"use client";
import { useState } from "react";
type Variant = "info" | "success" | "warning" | "error";
type AlertItem = {
id: string;
variant: Variant;
title: string;
message: string;
action?: { label: string; href: string };
};
const INITIAL_ALERTS: AlertItem[] = [
{
id: "deploy",
variant: "success",
title: "Deployment finished",
message: "webinnoventix.com is live on production. Propagation may take up to 5 minutes.",
action: { label: "View site", href: "https://webinnoventix.com" },
},
{
id: "ssl",
variant: "info",
title: "New SSL certificate issued",
message: "Your certificate auto-renews on 12 Oct 2026. No action is required from you.",
},
{
id: "usage",
variant: "warning",
title: "Bandwidth at 82% of monthly quota",
message: "You have used 41 GB of your 50 GB plan. Consider upgrading before the reset on the 1st.",
action: { label: "Manage plan", href: "https://webinnoventix.com/billing" },
},
{
id: "payment",
variant: "error",
title: "Payment method declined",
message: "We couldn't charge your card ending in 4417. Update it to avoid service interruption.",
action: { label: "Update card", href: "https://webinnoventix.com/billing" },
},
];
const VARIANT_STYLES: Record<
Variant,
{
container: string;
icon: string;
title: string;
body: string;
action: string;
close: string;
ring: string;
accent: string;
}
> = {
info: {
container:
"border-sky-200 bg-sky-50 dark:border-sky-900/60 dark:bg-sky-950/40",
icon: "text-sky-600 dark:text-sky-400",
title: "text-sky-900 dark:text-sky-100",
body: "text-sky-800/80 dark:text-sky-200/70",
action:
"text-sky-700 hover:text-sky-900 dark:text-sky-300 dark:hover:text-sky-100",
close:
"text-sky-500 hover:bg-sky-100 hover:text-sky-700 dark:text-sky-400 dark:hover:bg-sky-900/60 dark:hover:text-sky-200",
ring: "focus-visible:ring-sky-500",
accent: "bg-sky-500",
},
success: {
container:
"border-emerald-200 bg-emerald-50 dark:border-emerald-900/60 dark:bg-emerald-950/40",
icon: "text-emerald-600 dark:text-emerald-400",
title: "text-emerald-900 dark:text-emerald-100",
body: "text-emerald-800/80 dark:text-emerald-200/70",
action:
"text-emerald-700 hover:text-emerald-900 dark:text-emerald-300 dark:hover:text-emerald-100",
close:
"text-emerald-500 hover:bg-emerald-100 hover:text-emerald-700 dark:text-emerald-400 dark:hover:bg-emerald-900/60 dark:hover:text-emerald-200",
ring: "focus-visible:ring-emerald-500",
accent: "bg-emerald-500",
},
warning: {
container:
"border-amber-200 bg-amber-50 dark:border-amber-900/60 dark:bg-amber-950/40",
icon: "text-amber-600 dark:text-amber-400",
title: "text-amber-900 dark:text-amber-100",
body: "text-amber-800/80 dark:text-amber-200/70",
action:
"text-amber-700 hover:text-amber-900 dark:text-amber-300 dark:hover:text-amber-100",
close:
"text-amber-500 hover:bg-amber-100 hover:text-amber-700 dark:text-amber-400 dark:hover:bg-amber-900/60 dark:hover:text-amber-200",
ring: "focus-visible:ring-amber-500",
accent: "bg-amber-500",
},
error: {
container:
"border-rose-200 bg-rose-50 dark:border-rose-900/60 dark:bg-rose-950/40",
icon: "text-rose-600 dark:text-rose-400",
title: "text-rose-900 dark:text-rose-100",
body: "text-rose-800/80 dark:text-rose-200/70",
action:
"text-rose-700 hover:text-rose-900 dark:text-rose-300 dark:hover:text-rose-100",
close:
"text-rose-500 hover:bg-rose-100 hover:text-rose-700 dark:text-rose-400 dark:hover:bg-rose-900/60 dark:hover:text-rose-200",
ring: "focus-visible:ring-rose-500",
accent: "bg-rose-500",
},
};
function AlertIcon({ variant, className }: { variant: Variant; className?: string }) {
const common = {
className,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: 2,
strokeLinecap: "round" as const,
strokeLinejoin: "round" as const,
"aria-hidden": true,
};
switch (variant) {
case "success":
return (
<svg {...common}>
<path d="M20 6 9 17l-5-5" />
</svg>
);
case "warning":
return (
<svg {...common}>
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" />
<path d="M12 9v4" />
<path d="M12 17h.01" />
</svg>
);
case "error":
return (
<svg {...common}>
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>
);
default:
return (
<svg {...common}>
<circle cx="12" cy="12" r="10" />
<path d="M12 16v-4" />
<path d="M12 8h.01" />
</svg>
);
}
}
export default function AlertDismissible() {
const [alerts, setAlerts] = useState<AlertItem[]>(INITIAL_ALERTS);
function dismiss(id: string) {
setAlerts((prev) => prev.filter((a) => a.id !== id));
}
function reset() {
setAlerts(INITIAL_ALERTS);
}
return (
<section className="relative w-full bg-slate-50 px-4 py-16 sm:px-6 sm:py-20 dark:bg-slate-950">
<style>{`
@keyframes wivalert-in {
from { opacity: 0; transform: translateY(-8px) scale(0.985); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.wivalert-item { animation: wivalert-in 320ms cubic-bezier(0.16, 1, 0.3, 1) both; }
@media (prefers-reduced-motion: reduce) {
.wivalert-item { animation: none; }
}
`}</style>
<div className="mx-auto w-full max-w-2xl">
<header className="mb-8 flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
<div>
<h2 className="text-2xl font-semibold tracking-tight text-slate-900 dark:text-slate-50">
Notifications
</h2>
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
{alerts.length > 0
? `${alerts.length} message${alerts.length === 1 ? "" : "s"} need your attention`
: "You're all caught up"}
</p>
</div>
{alerts.length < INITIAL_ALERTS.length && (
<button
type="button"
onClick={reset}
className="inline-flex w-fit items-center gap-1.5 rounded-lg border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-600 transition-colors hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-950"
>
<svg
className="h-3.5 w-3.5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
<path d="M3 3v5h5" />
</svg>
Restore all
</button>
)}
</header>
<div className="flex flex-col gap-3">
{alerts.map((alert) => {
const s = VARIANT_STYLES[alert.variant];
return (
<div
key={alert.id}
role="alert"
className={`wivalert-item relative flex gap-3 overflow-hidden rounded-xl border p-4 pr-3 shadow-sm ${s.container}`}
>
<span
className={`absolute inset-y-0 left-0 w-1 ${s.accent}`}
aria-hidden
/>
<span className={`mt-0.5 shrink-0 ${s.icon}`}>
<AlertIcon variant={alert.variant} className="h-5 w-5" />
</span>
<div className="min-w-0 flex-1">
<p className={`text-sm font-semibold ${s.title}`}>
{alert.title}
</p>
<p className={`mt-0.5 text-sm leading-relaxed ${s.body}`}>
{alert.message}
</p>
{alert.action && (
<a
href={alert.action.href}
target="_blank"
rel="noopener"
className={`mt-2 inline-flex items-center gap-1 rounded text-sm font-semibold transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent ${s.action} ${s.ring}`}
>
{alert.action.label}
<svg
className="h-3.5 w-3.5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M7 7h10v10" />
<path d="M7 17 17 7" />
</svg>
</a>
)}
</div>
<button
type="button"
onClick={() => dismiss(alert.id)}
aria-label={`Dismiss: ${alert.title}`}
className={`h-8 w-8 shrink-0 rounded-lg transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-transparent ${s.close} ${s.ring}`}
>
<svg
className="mx-auto h-4 w-4"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button>
</div>
);
})}
{alerts.length === 0 && (
<div className="flex flex-col items-center gap-3 rounded-xl border border-dashed border-slate-300 bg-white/60 px-6 py-12 text-center dark:border-slate-800 dark:bg-slate-900/40">
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-emerald-100 text-emerald-600 dark:bg-emerald-950/60 dark:text-emerald-400">
<svg
className="h-6 w-6"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M20 6 9 17l-5-5" />
</svg>
</span>
<div>
<p className="text-sm font-semibold text-slate-900 dark:text-slate-100">
Inbox zero
</p>
<p className="mt-0.5 text-sm text-slate-500 dark:text-slate-400">
Every notification has been cleared.
</p>
</div>
<button
type="button"
onClick={reset}
className="mt-1 rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-slate-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200 dark:focus-visible:ring-offset-slate-950"
>
Restore notifications
</button>
</div>
)}
</div>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Soft Variants Alert
OriginalSoft-tinted alerts: success, info, warning and error, each with an icon.

Solid Variants Alert
OriginalSolid-colour alerts across the four states.

Outline Variants Alert
OriginalOutlined alerts across the four states.

Left Accent Alert
OriginalAlerts with a coloured left accent bar and icon.

With Action Alert
OriginalAn alert with title, body and action buttons.
Icon Large Alert
OriginalAn alert led by a large circular icon.

Banner Top Alert
OriginalA full-width top announcement banner, dismissible.

Toast Stack Alert
OriginalA stack of dismissible toasts in a corner.

Toast Slide Alert
OriginalA toast that slides in with an icon and auto-dismiss progress.

Progress Alert
OriginalAn alert with a countdown progress bar.

Gradient Alert
OriginalA gradient promo banner alert with a call to action.

Inline Form Alert
OriginalAn alert containing an inline email input and button.

