Outline Variants Alert
Original · freeOutlined alerts across the four states.
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-outline-variants.json"use client";
import { useState } from "react";
type AlertState = "success" | "error" | "warning" | "info";
interface AlertConfig {
id: string;
state: AlertState;
title: string;
body: string;
action: string;
}
const ALERTS: AlertConfig[] = [
{
id: "success",
state: "success",
title: "Payment received",
body: "Your invoice #INV-2047 was settled. A receipt is on its way to your inbox.",
action: "View receipt",
},
{
id: "error",
state: "error",
title: "Deployment failed",
body: "Build step exited with code 1. Check the logs for the failing test in api/auth.",
action: "Open logs",
},
{
id: "warning",
state: "warning",
title: "Storage almost full",
body: "You have used 92% of your plan's 50 GB. Free up space before uploads pause.",
action: "Manage storage",
},
{
id: "info",
state: "info",
title: "Scheduled maintenance",
body: "We are updating our servers on Sunday at 02:00 UTC. Expect a brief downtime.",
action: "Read more",
},
];
const STYLES: Record<
AlertState,
{
container: string;
icon: string;
title: string;
body: string;
action: string;
close: string;
ring: string;
}
> = {
success: {
container:
"border-emerald-300 bg-emerald-50/60 dark:border-emerald-500/40 dark:bg-emerald-500/5",
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 dark:text-emerald-400 dark:hover:bg-emerald-500/15",
ring: "focus-visible:ring-emerald-500/60",
},
error: {
container:
"border-rose-300 bg-rose-50/60 dark:border-rose-500/40 dark:bg-rose-500/5",
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 dark:text-rose-400 dark:hover:bg-rose-500/15",
ring: "focus-visible:ring-rose-500/60",
},
warning: {
container:
"border-amber-300 bg-amber-50/60 dark:border-amber-500/40 dark:bg-amber-500/5",
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 dark:text-amber-400 dark:hover:bg-amber-500/15",
ring: "focus-visible:ring-amber-500/60",
},
info: {
container:
"border-sky-300 bg-sky-50/60 dark:border-sky-500/40 dark:bg-sky-500/5",
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 dark:text-sky-400 dark:hover:bg-sky-500/15",
ring: "focus-visible:ring-sky-500/60",
},
};
function AlertIcon({ state, className }: { state: AlertState; className?: string }) {
const common = {
width: 20,
height: 20,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
strokeWidth: 2,
strokeLinecap: "round" as const,
strokeLinejoin: "round" as const,
className,
"aria-hidden": true,
};
if (state === "success") {
return (
<svg {...common}>
<circle cx="12" cy="12" r="9" />
<path d="m8.5 12 2.5 2.5 4.5-5" />
</svg>
);
}
if (state === "error") {
return (
<svg {...common}>
<circle cx="12" cy="12" r="9" />
<path d="M15 9l-6 6M9 9l6 6" />
</svg>
);
}
if (state === "warning") {
return (
<svg {...common}>
<path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" />
<path d="M12 9v4" />
<path d="M12 17h.01" />
</svg>
);
}
return (
<svg {...common}>
<circle cx="12" cy="12" r="9" />
<path d="M12 11v5" />
<path d="M12 8h.01" />
</svg>
);
}
export default function AlertOutlineVariants() {
const [visible, setVisible] = useState<Record<string, boolean>>({
success: true,
error: true,
warning: true,
info: true,
});
const dismiss = (id: string) => {
setVisible((prev) => ({ ...prev, [id]: false }));
};
const resetAll = () => {
setVisible({ success: true, error: true, warning: true, info: true });
};
const allHidden = ALERTS.every((a) => !visible[a.id]);
return (
<section className="relative w-full bg-white px-4 py-16 sm:px-6 sm:py-20 dark:bg-neutral-950">
<style>{`
@keyframes aov-enter {
0% { opacity: 0; transform: translateY(-8px) scale(0.985); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
.aov-item { animation: aov-enter 0.35s cubic-bezier(0.22, 1, 0.36, 1) both; }
@media (prefers-reduced-motion: reduce) {
.aov-item { animation: none; }
}
`}</style>
<div className="mx-auto max-w-2xl">
<div className="mb-8 flex items-end justify-between gap-4">
<div>
<p className="text-xs font-semibold uppercase tracking-widest text-indigo-600 dark:text-indigo-400">
Feedback
</p>
<h2 className="mt-1 text-2xl font-bold tracking-tight text-neutral-900 dark:text-neutral-50">
Outlined alerts
</h2>
<p className="mt-1 text-sm text-neutral-500 dark:text-neutral-400">
Four states, each dismissible with a primary action.
</p>
</div>
{allHidden && (
<button
type="button"
onClick={resetAll}
className="shrink-0 rounded-lg border border-neutral-300 bg-white px-3 py-1.5 text-xs font-medium text-neutral-700 transition-colors hover:bg-neutral-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500/60 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-200 dark:hover:bg-neutral-800"
>
Restore all
</button>
)}
</div>
<div className="space-y-3">
{ALERTS.map((alert) => {
if (!visible[alert.id]) return null;
const s = STYLES[alert.state];
return (
<div
key={alert.id}
role="alert"
className={`aov-item flex items-start gap-3 rounded-xl border px-4 py-3.5 ${s.container}`}
>
<span className={`mt-0.5 shrink-0 ${s.icon}`}>
<AlertIcon state={alert.state} />
</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.body}
</p>
<button
type="button"
onClick={() => dismiss(alert.id)}
className={`mt-2 rounded text-sm font-semibold underline-offset-4 transition-colors hover:underline focus:outline-none focus-visible:ring-2 ${s.action} ${s.ring}`}
>
{alert.action}
</button>
</div>
<button
type="button"
onClick={() => dismiss(alert.id)}
aria-label={`Dismiss ${alert.title}`}
className={`-mr-1 -mt-1 shrink-0 rounded-lg p-1.5 transition-colors focus:outline-none focus-visible:ring-2 ${s.close} ${s.ring}`}
>
<svg
width={16}
height={16}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden={true}
>
<path d="M18 6 6 18M6 6l12 12" />
</svg>
</button>
</div>
);
})}
</div>
{allHidden && (
<div className="rounded-xl border border-dashed border-neutral-300 px-4 py-10 text-center text-sm text-neutral-500 dark:border-neutral-700 dark:text-neutral-400">
All alerts dismissed.
</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.

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

Dismissible Alert
OriginalDismissible alerts with a working close button.

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.

