Skip to content
goodtabs UI
BrowseBrowse components

Alert banner

A full-width dismissible banner for page-level state, with an icon and an optional action.

npx shadcn@latest add https://ui.goodtabs.co/r/alert-banner.json

With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/alert-banner

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.

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

PropTypeDefaultDescription
tone"info" | "success" | "warning" | "error""info"Picks the icon and the spoken prefix. Warning and error use role=alert.
titlestringBold lead-in, same line as the message.
childrenReactNodeThe message. Keep it to one or two lines.
actionReactNodeOne action on the right, usually a ghost sm Button.
onDismiss() => voidAdds the dismiss button. Called after the banner hides itself.
dismissLabelstring"Dismiss banner"Accessible name of the dismiss button.

Source

alert-banner/alert-banner.tsx
"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