Alert
Inline alert banners for feedback messages — info, success, warning, and danger variants with icons, accent borders, links, actions, and dismissible support.
Preview
Basic alert
import { Alert, AlertTitle, AlertDescription } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<Alert variant="info">
<AlertTitle>New update available</AlertTitle>
<AlertDescription>A new software update is available for download.</AlertDescription>
</Alert>
)
}All variants
import { Alert, AlertDescription } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<div className="space-y-3">
<Alert variant="info">
<AlertDescription>This is an informational message.</AlertDescription>
</Alert>
<Alert variant="success">
<AlertDescription>Changes saved successfully.</AlertDescription>
</Alert>
<Alert variant="warning">
<AlertDescription>You are approaching the rate limit.</AlertDescription>
</Alert>
<Alert variant="danger">
<AlertDescription>Something went wrong. Please try again.</AlertDescription>
</Alert>
</div>
)
}Accent border
import { Alert, AlertTitle, AlertDescription } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<Alert variant="success" accent>
<AlertTitle>Deployment complete</AlertTitle>
<AlertDescription>Your application has been deployed to production.</AlertDescription>
</Alert>
)
}With link
import { Alert, AlertDescription, AlertLink } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<Alert variant="warning">
<AlertDescription>
Your subscription expires in 3 days.{' '}
<AlertLink variant="warning" href="/billing">Renew now</AlertLink>
</AlertDescription>
</Alert>
)
}With actions
import { Alert, AlertTitle, AlertDescription, AlertActions } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<Alert variant="danger">
<AlertTitle>Delete workspace?</AlertTitle>
<AlertDescription>This action cannot be undone.</AlertDescription>
<AlertActions>
<button className="rounded-md bg-rose-600 px-3 py-1.5 text-xs font-medium text-white">
Delete
</button>
<button className="text-xs font-medium text-rose-700">Cancel</button>
</AlertActions>
</Alert>
)
}Dismissible
import { Alert, AlertTitle, AlertDescription } from '@/registry/ui-kit/tailwind-alert/react'
function Example() {
return (
<Alert variant="info" dismissible onDismiss={() => console.log('dismissed')}>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>Click the X button to dismiss this alert.</AlertDescription>
</Alert>
)
}Component API
| Prop | Default | Description |
|---|---|---|
variant | "info" | Visual style of the alert. |
icon | auto | Custom icon element. Pass null to hide the icon. Defaults to a variant-matched icon. |
accent | false | Show a colored left accent border. |
dismissible | false | Show a close button that removes the alert. |
onDismiss | — | Callback fired when the alert is dismissed. |
AlertTitle | — | Bold heading text inside the alert. |
AlertDescription | — | Body text inside the alert. |
AlertActions | — | Wrapper for action buttons below the description. |
AlertLink | — | Styled inline link. Accepts a variant prop for color matching. |
Source Code
"use client";
import { forwardRef, useState } from "react";
/* ── Variant styles ── */
const variantStyles = {
info: {
container: "border-sky-200 bg-sky-50 dark:border-sky-500/30 dark:bg-sky-950/40",
accent: "bg-sky-500",
icon: "text-sky-600 dark:text-sky-400",
title: "text-sky-900 dark:text-sky-100",
description: "text-sky-800 dark:text-sky-200",
close: "text-sky-500 hover:bg-sky-100 dark:hover:bg-sky-900/40",
link: "text-sky-700 underline decoration-sky-700/30 hover:decoration-sky-700 dark:text-sky-300 dark:decoration-sky-300/30 dark:hover:decoration-sky-300",
},
success: {
container: "border-emerald-200 bg-emerald-50 dark:border-emerald-500/30 dark:bg-emerald-950/40",
accent: "bg-emerald-500",
icon: "text-emerald-600 dark:text-emerald-400",
title: "text-emerald-900 dark:text-emerald-100",
description: "text-emerald-800 dark:text-emerald-200",
close: "text-emerald-500 hover:bg-emerald-100 dark:hover:bg-emerald-900/40",
link: "text-emerald-700 underline decoration-emerald-700/30 hover:decoration-emerald-700 dark:text-emerald-300 dark:decoration-emerald-300/30 dark:hover:decoration-emerald-300",
},
warning: {
container: "border-amber-200 bg-amber-50 dark:border-amber-500/30 dark:bg-amber-950/40",
accent: "bg-amber-500",
icon: "text-amber-600 dark:text-amber-400",
title: "text-amber-900 dark:text-amber-100",
description: "text-amber-800 dark:text-amber-200",
close: "text-amber-500 hover:bg-amber-100 dark:hover:bg-amber-900/40",
link: "text-amber-700 underline decoration-amber-700/30 hover:decoration-amber-700 dark:text-amber-300 dark:decoration-amber-300/30 dark:hover:decoration-amber-300",
},
danger: {
container: "border-rose-200 bg-rose-50 dark:border-rose-500/30 dark:bg-rose-950/40",
accent: "bg-rose-500",
icon: "text-rose-600 dark:text-rose-400",
title: "text-rose-900 dark:text-rose-100",
description: "text-rose-800 dark:text-rose-200",
close: "text-rose-500 hover:bg-rose-100 dark:hover:bg-rose-900/40",
link: "text-rose-700 underline decoration-rose-700/30 hover:decoration-rose-700 dark:text-rose-300 dark:decoration-rose-300/30 dark:hover:decoration-rose-300",
},
};
type AlertVariant = keyof typeof variantStyles;
/* ── Icons ── */
function InfoIcon({ className }: { className?: string }) {
return (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" />
<path d="M12 16v-4" />
<path d="M12 8h.01" />
</svg>
);
}
function CheckCircleIcon({ className }: { className?: string }) {
return (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<path d="m9 11 3 3L22 4" />
</svg>
);
}
function TriangleAlertIcon({ className }: { className?: string }) {
return (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
<path d="M12 9v4" />
<path d="M12 17h.01" />
</svg>
);
}
function XCircleIcon({ className }: { className?: string }) {
return (
<svg className={className} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" />
<path d="m15 9-6 6" />
<path d="m9 9 6 6" />
</svg>
);
}
const defaultIcons: Record<AlertVariant, React.ComponentType<{ className?: string }>> = {
info: InfoIcon,
success: CheckCircleIcon,
warning: TriangleAlertIcon,
danger: XCircleIcon,
};
/* ── Alert ── */
interface AlertProps {
variant?: AlertVariant;
icon?: React.ReactNode;
accent?: boolean;
dismissible?: boolean;
onDismiss?: () => void;
children: React.ReactNode;
className?: string;
}
const Alert = forwardRef<HTMLDivElement, AlertProps>(
({ variant = "info", icon, accent = false, dismissible = false, onDismiss, children, className = "" }, ref) => {
const [visible, setVisible] = useState(true);
const styles = variantStyles[variant];
const DefaultIcon = defaultIcons[variant];
if (!visible) return null;
const handleDismiss = () => {
setVisible(false);
onDismiss?.();
};
const resolvedIcon = icon !== undefined ? icon : <DefaultIcon className={`h-5 w-5 shrink-0 ${styles.icon}`} />;
return (
<div
ref={ref}
role="alert"
className={`relative flex gap-3 overflow-hidden rounded-lg border p-4 ${styles.container} ${className}`}
>
{accent && (
<div className={`absolute inset-y-0 left-0 w-1 ${styles.accent}`} />
)}
<div className={accent ? "pl-1" : ""}>
{resolvedIcon}
</div>
<div className="flex-1 space-y-1">
{children}
</div>
{dismissible && (
<button
type="button"
onClick={handleDismiss}
className={`-mr-1 -mt-1 inline-flex shrink-0 rounded-md p-1 transition-colors ${styles.close}`}
aria-label="Dismiss"
>
<svg className="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 6 6 18" />
<path d="m6 6 12 12" />
</svg>
</button>
)}
</div>
);
}
);
Alert.displayName = "Alert";
/* ── AlertTitle ── */
function AlertTitle({ children, className = "" }: { children: React.ReactNode; className?: string }) {
return <h5 className={`text-sm font-medium ${className}`}>{children}</h5>;
}
/* ── AlertDescription ── */
function AlertDescription({ children, className = "" }: { children: React.ReactNode; className?: string }) {
return <p className={`text-sm ${className}`}>{children}</p>;
}
/* ── AlertActions ── */
function AlertActions({ children, className = "" }: { children: React.ReactNode; className?: string }) {
return <div className={`mt-2 flex items-center gap-3 ${className}`}>{children}</div>;
}
/* ── AlertLink ── */
interface AlertLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
variant?: AlertVariant;
children: React.ReactNode;
}
function AlertLink({ variant = "info", children, className = "", ...props }: AlertLinkProps) {
const styles = variantStyles[variant];
return (
<a className={`font-medium transition-colors ${styles.link} ${className}`} {...props}>
{children}
</a>
);
}
export { Alert, AlertTitle, AlertDescription, AlertActions, AlertLink };
export type { AlertVariant };
export default Alert;