Overview
The Alert (also called a banner, callout, notification banner, or inline notification) is a feedback component that displays an important message to the user without interrupting their workflow. Unlike a Dialog (which blocks interaction) or a Toast (which auto-dismisses), an alert is persistent, inline, and non-modal — it sits within the page content and stays visible until the user addresses it or the condition changes.
Alerts communicate status: success after an action, a warning about something that needs attention, an error that requires correction, or informational context that helps the user understand their situation. They're the "yellow sticky note on the monitor" of the digital world — visible, persistent, but not blocking.
When to use an Alert:
- Communicating the result of an action ("Your changes have been saved successfully")
- Warning about potential issues ("Your subscription expires in 3 days")
- Displaying errors that affect the entire page or form ("Unable to load your data. Please try again.")
- Providing informational context ("This feature is in beta. Some functionality may change.")
- System-wide announcements ("Scheduled maintenance on March 15, 2026")
When NOT to use an Alert:
- For transient success feedback — use a Toast that auto-dismisses
- For errors on specific form fields — use inline validation on the Text Input itself
- For critical actions requiring user choice — use a Dialog or Alert Dialog
- For permanent, ever-present information — use a callout or aside (not styled as an alert)
- For notification counts or badges — use a Badge
Alert vs. Toast decision: If the message is the result of a user action and can be missed without consequence, use a Toast. If the message describes a state the user needs to be aware of (and should see even if they weren't looking when it appeared), use an Alert.
Style your alert's border-radius with the Border Radius Generator and choose semantically appropriate color palettes with the Color Palette Generator. Always verify text contrast with the Contrast Checker.
Variants
Semantic Variants
| Variant | Color | Icon | Purpose |
|---|---|---|---|
| Info | Blue | ℹ️ (information circle) | Neutral information, tips, context. No urgency. |
| Success | Green | ✓ (checkmark circle) | Confirmation that an action completed successfully. |
| Warning | Yellow/Amber | ⚠️ (triangle exclamation) | Something needs attention but isn't blocking. |
| Error | Red | ✕ (X circle) or ❗ | Something went wrong. Action may be required. |
Visual Variants
| Variant | Description |
|---|---|
| Filled | Solid background color (light tint of the semantic color). High visibility. |
| Outlined | White/transparent background with a colored left border (4px). Subtle but clear. |
| Soft | Very light tinted background with matching text color. Minimal visual weight. |
| Banner | Full-width, typically at the top of the page or content area. No border-radius. |
| Inline | Sits within the content flow. Has border-radius. Standard width. |
Structural Variants
| Variant | Description |
|---|---|
| Simple | Icon + single line of text |
| With title | Icon + bold title + description text |
| With actions | Icon + text + action buttons or links ("Retry", "Dismiss", "Learn more") |
| With list | Icon + title + bulleted list of items (e.g., multiple form errors) |
| Dismissible | Includes a close (✕) button to remove the alert |
Size Variants
| Size | Padding | Font Size | Use Case |
|---|---|---|---|
| Small | 8px 12px | 13px | Inline field-level messages, compact UIs |
| Medium | 12px 16px | 14px | Default page-level alerts |
| Large | 16px 20px | 16px | Full-width banners, critical system alerts |
Properties
Alert Properties
| Property | Type | Default | Description |
|---|---|---|---|
variant | 'info' | 'success' | 'warning' | 'error' | 'info' | Semantic variant controlling color and icon |
title | string | — | Bold heading text |
children | ReactNode | — | Alert body content |
icon | ReactNode | false | Auto (based on variant) | Custom icon. Set false to hide. |
dismissible | boolean | false | Shows a close button |
onDismiss | () => void | — | Callback when dismissed |
actions | ReactNode | — | Action buttons or links rendered in the alert footer |
role | 'alert' | 'status' | 'none' | 'status' | ARIA role. Use 'alert' for urgent messages, 'status' for informational. |
Token Mappings
Design Token Mappings
| Token Category | Token Example | Alert Usage |
|---|---|---|
| Color – Info Background | --color-info-50 | Info variant background |
| Color – Info Border | --color-info-200 | Info variant left border |
| Color – Info Icon | --color-info-600 | Info icon color |
| Color – Info Text | --color-info-800 | Info title color |
| Color – Success | --color-success-50 / -200 / -600 / -800 | Success variant tokens |
| Color – Warning | --color-warning-50 / -200 / -600 / -800 | Warning variant tokens |
| Color – Error | --color-error-50 / -200 / -600 / -800 | Error variant tokens |
| Border Radius | --radius-md (8px) or --radius-lg (12px) | Alert container corners. Preview with Border Radius Generator. |
| Border – Left Accent | 4px solid --color-[variant]-500 | Outlined variant left border |
| Spacing | --space-3 (12px), --space-4 (16px) | Internal padding |
| Spacing – Gap | --space-3 (12px) | Gap between icon, text, and actions |
| Typography – Title | --font-size-sm, --font-weight-semibold | Alert title |
| Typography – Body | --font-size-sm, --font-weight-normal | Alert description |
Use the Color Palette Generator to generate consistent semantic color scales for all four variants.
States
Alert States
| State | Behavior |
|---|---|
| Visible | Alert is displayed inline in the content flow. Content before and after adjusts. |
| Entering | Fade in + slide down (optional). 200ms ease-out. |
| Dismissing | Fade out + slide up. On completion, the alert is removed from the DOM (not just hidden). Content below shifts up. |
| Dismissed | Alert is removed. If server-driven, the dismissal state may be persisted. |
Contextual States
| Context | Behavior |
|---|---|
| Page-level | Alert appears at the top of the content area, below the header. Full width of the content column. |
| Section-level | Alert appears within a specific section (form, card, panel). Scoped to that section's width. |
| Banner | Full viewport width, fixed or sticky at the top of the page. Used for system announcements. |
| Form error summary | Special alert at the top of a form listing all validation errors. Each error links to its field. |
Dynamic Alerts
Alerts that appear dynamically (after an API call, form submission, or system event) should be announced to screen readers. This is where role="alert" or role="status" becomes critical:
role="alert": Assertive. Screen readers interrupt whatever they're reading to announce the alert. Use for errors and urgent warnings.role="status": Polite. Screen readers announce it after finishing the current sentence. Use for success messages and informational updates.- Neither: For alerts that are present on page load (not dynamically inserted), no ARIA role is needed — the user will encounter them naturally while reading.
Accessibility
WCAG Requirements
| Criterion | Level | Requirement |
|---|---|---|
| SC 1.4.1 Use of Color | A | Don't rely on color alone to convey the alert type. Include an icon (info, warning, error, success) alongside the color. |
| SC 1.4.3 Contrast (Minimum) | AA | Alert text must have 4.5:1 contrast against the alert background. This is frequently failed — dark text on a light tint needs careful calibration. Use Contrast Checker for every variant. |
| SC 1.4.11 Non-text Contrast | AA | Icons and borders: 3:1 against adjacent background. |
| SC 4.1.3 Status Messages | AA | Dynamic alerts must be announced to screen readers without receiving focus. Use role="alert" or role="status". |
| SC 2.2.1 Timing Adjustable | A | If the alert auto-dismisses, users must be able to extend the time or the alert must be available elsewhere. Consider making alerts persistent by default. |
ARIA Implementation
For dynamically injected alerts:
<!-- Error: assertive announcement -->
<div role="alert">
<strong>Error:</strong> Unable to save your changes. Please try again.
</div>
<!-- Success: polite announcement -->
<div role="status">
Your profile has been updated successfully.
</div>
For alerts present on page load:
<!-- No role needed — content is in the natural reading flow -->
<div class="alert alert-info">
<p>This feature is currently in beta.</p>
</div>
Dismissible Alert Accessibility
- The dismiss button must have
aria-label="Dismiss"oraria-label="Close alert" - After dismissal, focus should move to a logical location (the next element in the flow, or the preceding heading)
- The dismissal should be announced: inject a visually-hidden "Alert dismissed" text in an
aria-liveregion
Color Contrast Per Variant
This is the biggest accessibility pitfall with alerts. Typical failures:
| Variant | Common Failure | Fix |
|---|---|---|
| Success | Dark green text on light green bg fails 4.5:1 | Use --color-success-800 on --color-success-50 — verify exact values |
| Warning | Orange text on yellow bg almost always fails | Use dark amber or near-black text on light yellow |
| Info | Light blue text on light blue bg | Use --color-info-800 for text, not --color-info-500 |
| Error | Usually passes — red on light pink has inherently higher contrast | Still verify |
Always test every variant in both light and dark mode. The Contrast Checker supports both.
For comprehensive patterns, see our WCAG Practical Guide and ARIA Attributes Guide.
Usage Guidelines
Do's
- ✅ Use the correct semantic variant. Don't use a warning alert for an error, or an info alert for a success message. The color and icon should match the meaning.
- ✅ Lead with the most important information. "Your account has been suspended" first, explanation second.
- ✅ Include an action when possible. "Your session expires in 5 minutes. [Extend session]" is more useful than just the warning.
- ✅ Use alert as a form error summary. At the top of the form, show "There are 3 errors below" with links to each field. This is especially valuable for long forms.
- ✅ Keep alert text concise. One sentence for the title, 1–2 sentences for the description. If you need more, link to a detail page.
- ✅ Make page-level error alerts persistent until the error is resolved. Don't auto-dismiss errors.
Don'ts
- ❌ Don't stack more than 2–3 alerts at once. If you have that many, consolidate or redesign the page state.
- ❌ Don't use alerts for marketing. "New feature! Check it out!" is a banner, not an alert. Alerts are for status and feedback.
- ❌ Don't auto-dismiss error alerts. The user needs time to read, understand, and act. Success alerts can auto-dismiss; errors should not.
- ❌ Don't place alerts at the bottom of the page where users might not see them. Position them near the relevant content (top of the form, inside the affected section).
- ❌ Don't use an alert where a Toast would suffice. If it's brief feedback that doesn't require action ("Copied to clipboard"), a Toast is less intrusive.
- ❌ Don't use colored text on colored backgrounds without checking contrast. This is the #1 accessibility failure in alert components. Validate every color combination with the Contrast Checker.
Content Guidelines
- Title: Short, descriptive, not alarming ("Payment method expiring" not "URGENT: PAYMENT ISSUE!!!")
- Description: Explain the situation and what the user can do about it
- Action labels: Verb-first, specific ("Update payment method", "View details", "Try again")
- Error lists: Link each error to the corresponding field: "Email address is required" → clicking scrolls to and focuses the email field
Code Snippets
<!-- Info Alert -->
<div class="alert alert-info" role="status">
<svg class="alert-icon" aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.25v2.75a.75.75 0 0 0 1.5 0V10A.75.75 0 0 0 10 9H9Z" clip-rule="evenodd"/>
</svg>
<div class="alert-content">
<p class="alert-title">New feature available</p>
<p class="alert-description">Dark mode is now available in settings. <a href="/settings">Try it out</a>.</p>
</div>
</div>
<!-- Error Alert with dismiss -->
<div class="alert alert-error" role="alert">
<svg class="alert-icon" aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd"/>
</svg>
<div class="alert-content">
<p class="alert-title">Unable to save changes</p>
<p class="alert-description">There was a network error. Please check your connection and try again.</p>
</div>
<button type="button" class="alert-dismiss" aria-label="Dismiss alert">
<svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path d="M6.28 5.22a.75.75 0 0 0-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 1 0 1.06 1.06L10 11.06l3.72 3.72a.75.75 0 1 0 1.06-1.06L11.06 10l3.72-3.72a.75.75 0 0 0-1.06-1.06L10 8.94 6.28 5.22Z"/>
</svg>
</button>
</div>
<!-- Form Error Summary Alert -->
<div class="alert alert-error" role="alert">
<svg class="alert-icon" aria-hidden="true" width="20" height="20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd"/>
</svg>
<div class="alert-content">
<p class="alert-title">There are 3 errors in this form</p>
<ul class="alert-list">
<li><a href="#email">Email address is required</a></li>
<li><a href="#password">Password must be at least 8 characters</a></li>
<li><a href="#terms">You must agree to the terms of service</a></li>
</ul>
</div>
</div>import { useState, type ReactNode } from "react";
type AlertVariant = "info" | "success" | "warning" | "error";
interface AlertProps {
variant?: AlertVariant;
title?: string;
children: ReactNode;
icon?: ReactNode | false;
dismissible?: boolean;
onDismiss?: () => void;
actions?: ReactNode;
role?: "alert" | "status" | "none";
}
const defaultIcons: Record<AlertVariant, ReactNode> = {
info: <InfoIcon />,
success: <CheckCircleIcon />,
warning: <WarningIcon />,
error: <ErrorIcon />,
};
export default function Alert({
variant = "info",
title,
children,
icon,
dismissible = false,
onDismiss,
actions,
role: ariaRole = "status",
}: AlertProps) {
const [visible, setVisible] = useState(true);
if (!visible) return null;
const handleDismiss = () => {
setVisible(false);
onDismiss?.();
};
const alertIcon = icon === false ? null : icon ?? defaultIcons[variant];
return (
<div
className={`alert alert-${variant}`}
role={ariaRole !== "none" ? ariaRole : undefined}
>
{alertIcon && (
<span className="alert-icon" aria-hidden="true">
{alertIcon}
</span>
)}
<div className="alert-content">
{title && <p className="alert-title">{title}</p>}
<div className="alert-description">{children}</div>
{actions && <div className="alert-actions">{actions}</div>}
</div>
{dismissible && (
<button
type="button"
className="alert-dismiss"
aria-label="Dismiss alert"
onClick={handleDismiss}
>
<CloseIcon aria-hidden="true" />
</button>
)}
</div>
);
}
// Usage
<Alert variant="error" title="Unable to save changes" dismissible>
<p>There was a network error. Please check your connection and try again.</p>
</Alert>
<Alert
variant="warning"
title="Subscription expiring"
actions={
<button className="btn btn-sm btn-primary" onClick={handleRenew}>
Renew now
</button>
}
>
<p>Your subscription expires in 3 days.</p>
</Alert>
<Alert variant="success" role="status">
Your profile has been updated successfully.
</Alert>Design Systems
Cross-System Comparison
| Feature | Material 3 | Shadcn/ui | Radix | Ant Design |
|---|---|---|---|---|
| Component | Snackbar (transient) / no direct Alert | Alert (styled div) | No alert primitive | Alert, Notification |
| Variants | No semantic variants | default + destructive only | N/A | success, info, warning, error |
| Dismissible | Snackbar: auto-dismiss | Manual close button | N/A | closable prop |
| Icon | Manual | Manual | N/A | showIcon prop + auto per variant |
| Banner mode | Not built-in | Not built-in | N/A | banner prop (full-width, no icon/border) |
| Actions | Snackbar action button | Manual | N/A | action slot |
| Animation | Material Motion | None (static) | N/A | Ant Motion (slide) |
| Accessibility | Basic | Manual role | N/A | role="alert" by default |
Notable Approaches
Ant Design Alert is the most complete implementation. It supports all four semantic variants with automatic icons, a banner mode that stretches full-width with no border-radius (for page-level announcements), a closable prop with afterClose callback, and a description prop that separates the title from the body. It also supports a message array for showing multiple messages in one alert.
Shadcn/ui keeps alerts minimal — just default and destructive variants, styled with Tailwind. This is intentional: they treat alerts as styled containers and expect you to compose the content (icons, titles, descriptions, actions) manually. It's flexible but requires more work for each usage.
Material 3 doesn't have a direct "Alert" component — Material philosophy prefers Snackbars (transient) for feedback and Banners (persistent, full-width) for status messages. If you need a classic inline alert in a Material system, you compose it from a Card or custom component.
Radix has no alert primitive because alerts are purely presentational containers with ARIA attributes — there's no complex interaction behavior to abstract. The ARIA part (role="alert", role="status") is trivial to add to a styled <div>.
Bootstrap's Alert deserves a mention for its influence: it established the four-color (info/success/warning/danger) convention that virtually every design system has adopted. The pattern is so universal that users now intuitively understand blue = info, green = success, yellow = warning, red = error without any labels.
For accessible color palettes that work across all four variants in both light and dark mode, use our Color Palette Generator to generate consistent scales.