Shadcn Alert for React and Tailwind
Displays an alert for user attention.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/alert.jsonpnpm dlx shadcn@latest add https://kit.dev/r/alert.jsonnpx shadcn@latest add https://kit.dev/r/alert.jsonyarn shadcn@latest add https://kit.dev/r/alert.jsonInstall the following dependencies:
bun add @ark-ui/react tailwind-variantspnpm add @ark-ui/react tailwind-variantsnpm install @ark-ui/react tailwind-variantsyarn add @ark-ui/react tailwind-variantsImport the following variables into your CSS file
@theme inline {
--color-destructive-foreground: var(--destructive-foreground);
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
}
:root {
--destructive-foreground: var(--color-red-700);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-700);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
}
.dark {
--destructive-foreground: var(--color-red-400);
--info: var(--color-blue-500);
--info-foreground: var(--color-blue-400);
--success: var(--color-emerald-500);
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
}Copy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
export const alertVariants = tv({
base: [
"relative",
"px-3.5 py-3",
"grid w-full items-start gap-x-2 gap-y-0.5",
"text-card-foreground text-sm",
"rounded-xl border",
"has-[>svg]:has-data-[slot=alert-action]:grid-cols-[--spacing(4)_1fr_auto] has-[>svg]:grid-cols-[--spacing(4)_1fr]",
"has-[>svg]:gap-x-2 [&_svg]:h-lh [&_svg]:w-4",
"has-data-[slot=alert-action]:grid-cols-[1fr_auto]",
],
variants: {
variant: {
default: [
"bg-input/4",
"[&_svg]:text-muted-foreground",
"[&_[data-slot=alert-action]_[data-variant=ghost]]:hover:bg-muted",
],
destructive: [
"bg-destructive/4",
"border-destructive/32",
"[&_svg]:text-destructive",
"[&_[data-slot=alert-action]_[data-variant=ghost]]:hover:bg-destructive/10",
],
info: [
"bg-info/4",
"border-info/32",
"[&_svg]:text-info",
"[&_[data-slot=alert-action]_[data-variant=ghost]]:hover:bg-info/10",
],
warning: [
"bg-warning/4",
"border-warning/32",
"[&_svg]:text-warning",
"[&_[data-slot=alert-action]_[data-variant=ghost]]:hover:bg-warning/10",
],
success: [
"bg-success/4",
"border-success/32",
"[&_svg]:text-success",
"[&_[data-slot=alert-action]_[data-variant=ghost]]:hover:bg-success/10",
],
},
},
defaultVariants: {
variant: "default",
},
});
interface AlertProps
extends React.ComponentProps<typeof ark.div>,
VariantProps<typeof alertVariants> {}
export const Alert = (props: AlertProps) => {
const { variant, className, role = "alert", ...rest } = props;
return (
<ark.div
className={cn(alertVariants({ variant }), className)}
data-slot="alert"
data-variant={variant ?? "default"}
role={role}
{...rest}
/>
);
};
export const AlertTitle = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"font-heading font-medium",
"[svg~&]:col-start-2",
className
)}
data-slot="alert-title"
{...rest}
/>
);
};
export const AlertDescription = (
props: React.ComponentProps<typeof ark.div>
) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex flex-col gap-2.5",
"text-muted-foreground",
"[svg~&]:col-start-2",
className
)}
data-slot="alert-description"
{...rest}
/>
);
};
export const AlertAction = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex gap-1",
"max-sm:col-start-2 max-sm:mt-2",
"sm:[svg~[data-slot=alert-title]~&]:col-start-3",
"sm:row-start-1 sm:row-end-3 sm:self-center",
"sm:[[data-slot=alert-description]~&]:col-start-2",
"sm:[[data-slot=alert-title]~&]:col-start-2",
"sm:[svg~&]:col-start-2",
"sm:[svg~[data-slot=alert-description]~&]:col-start-3",
className
)}
data-slot="alert-action"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
The icon is any direct svg child (typically a Lucide icon). AlertAction is optional.
Alert
├── Icon (optional)
├── AlertTitle
├── AlertDescription
└── AlertAction (optional)Usage
import {
Alert,
AlertAction,
AlertDescription,
AlertTitle,
} from "@/components/ui/alert";<Alert>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>You can add icons to alerts.</AlertDescription>
<AlertAction>
<Button size="xs" variant="ghost">Dismiss</Button>
<Button size="xs">Update</Button>
</AlertAction>
</Alert>Variants
Default
Info
Warning
Success
Destructive
Examples
With icon
With action
Custom color
API Reference
asChild merges props onto a single child element.
Place a Lucide (or other) icon as a direct child of Alert, before the title. The root uses CSS :has(> svg) to reserve a column for it.
Alert
Root. Renders a div with role="alert" by default.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "info" | "warning" | "success" | "destructive" | "default" | Color and icon treatment. |
role | string | "alert" | ARIA role. Use "status" for non-urgent, non-interruptive messages. |
asChild | boolean | false | Render the child element instead of a div. |
className | string | - | Class names on the root. |
| Attribute | Description |
|---|---|
data-slot | alert |
data-variant | "default", "info", "warning", "success", or "destructive" |
role | "alert" unless overridden |
AlertTitle
Heading of the message. Renders a div. Sits in the second grid column when an icon is present.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child (for example h2). |
className | string | - | Class names on the title. |
| Attribute | Description |
|---|---|
data-slot | alert-title |
AlertDescription
Supporting text. Renders a div. Muted foreground; stacks extra content with flex-col gap-2.5.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the description. |
| Attribute | Description |
|---|---|
data-slot | alert-description |
AlertAction
Slot for buttons or other controls. On sm and up it sits on the trailing edge and spans the title/description rows. Below sm it wraps under the text.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merge onto a single child. |
className | string | - | Class names on the action slot. |
| Attribute | Description |
|---|---|
data-slot | alert-action |
Ghost buttons inside the action slot pick up a variant-tinted hover from the root (data-variant).
Accessibility
Follows the Alert WAI-ARIA pattern. The root defaults to role="alert" (assertive live region). For quiet confirmations, set role="status".
Do not put the only copy of severity in a decorative icon. If the title already says “Error” or “Success”, mark the icon aria-hidden="true". If the icon is the only severity cue, leave it exposed to assistive tech.
Keyboard support
Alert is not a composite widget. Focusable controls inside AlertAction participate in the normal tab order.
| Key | Description |
|---|---|
Tab | Move to the next control in the action slot. |
Shift + Tab | Move to the previous control. |