Alert banner
A full-width dismissible banner for page-level state, with an icon and an optional action.
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/alert-banner
Warning: Sync is paused. Reconnect to resume.
A warning with an action and a dismiss button, sitting at the top of a page.
Tones
Info: Scheduled maintenance. The dashboard is read only on Sunday from 02:00 to 03:00 UTC.
Success: Deploy finished. Version 2.4.1 is live in all regions.
Warning: Trial ends in 3 days. Add a card to keep your projects.
Error: Payment failed. We could not charge the card ending 4242.
Info, success, warning and error. Each has its own icon and spoken prefix.
Usage
import { AlertBanner } from "@/components/ui/alert-banner"
<AlertBanner tone="error" title="Payment failed." onDismiss={() => {}}>
We could not charge the card ending 4242.
</AlertBanner>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "info" | "success" | "warning" | "error" | "info" | Picks the icon and the spoken prefix. Warning and error use role=alert. |
| title | string | Bold lead-in, same line as the message. | |
| children | ReactNode | The message. Keep it to one or two lines. | |
| action | ReactNode | One action on the right, usually a ghost sm Button. | |
| onDismiss | () => void | Adds the dismiss button. Called after the banner hides itself. | |
| dismissLabel | string | "Dismiss banner" | Accessible name of the dismiss button. |
Source
"use client";
import * as React from "react";
import { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, XIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
type Tone = "info" | "success" | "warning" | "error";
type AlertBannerProps = Omit<React.ComponentProps<"div">, "title" | "children"> & {
/** Picks the icon and the spoken prefix. */
tone?: Tone;
/** Bold lead, then the rest of the message in the same line. */
title?: string;
children: React.ReactNode;
/** One action on the right, usually a ghost sm Button. */
action?: React.ReactNode;
/** Adds the dismiss button. The banner removes itself, then calls this. */
onDismiss?: () => void;
dismissLabel?: string;
};
const ICON = { info: InfoIcon, success: CircleCheckIcon, warning: TriangleAlertIcon, error: CircleAlertIcon } as const;
const LABEL = { info: "Info", success: "Success", warning: "Warning", error: "Error" } as const;
/**
* A full-width, dismissible banner for the state of the whole page: an outage, a failed sync,
* an expiring plan. State is the icon and a spoken prefix, never colour. Errors tint the icon with destructive.
*/
export function AlertBanner({
tone = "info",
title,
children,
action,
onDismiss,
dismissLabel = "Dismiss banner",
className,
...props
}: AlertBannerProps) {
const [dismissed, setDismissed] = React.useState(false);
if (dismissed) return null;
const Icon = ICON[tone];
const urgent = tone === "error" || tone === "warning";
return (
<div
role={urgent ? "alert" : "status"}
data-slot="alert-banner"
data-tone={tone}
className={cn("flex w-full items-center gap-3 border-b bg-muted px-4 py-2 text-sm text-foreground", className)}
{...props}
>
<Icon aria-hidden="true" className={cn("size-4 shrink-0", tone === "error" && "text-destructive")} />
<p className="m-0 min-w-0 flex-1">
<span className="sr-only">{LABEL[tone]}: </span>
{title && <span className="font-medium">{title} </span>}
<span className="text-foreground/70">{children}</span>
</p>
{action && <div className="shrink-0">{action}</div>}
{onDismiss && (
<Button
type="button"
variant="ghost"
size="icon-sm"
aria-label={dismissLabel}
onClick={() => {
setDismissed(true);
onDismiss();
}}
className="-mr-1.5 shrink-0 rounded-full"
>
<XIcon aria-hidden="true" />
</Button>
)}
</div>
);
}
For AI agents
Hand your agent this page, the markdown at /components/alert-banner/prompt.md, or the Copy AI prompt button above.
When to use
- Something about the whole page or account needs attention
- An outage, failed sync, expiring trial or read-only mode
- A state that should be easy to dismiss but hard to miss
When not to use
- Promotions and launches, use the announcement bar
- A problem with one field or one section, use an inline alert
- Confirming a quick action, use a toast
Agent notes
- Client component; a demo that passes onDismiss must be a client component too
- Dismissing hides it for this render only; persist the choice in onDismiss if it should stay gone
- No colour carries state: the icon shape and a screen reader prefix do. Only the error icon uses text-destructive
- Different from announcement-bar: this one reports state, has tones, a bottom hairline and an action slot
- It has no radius, so put it flush at the top of the page or inside an overflow-hidden frame