Alert

Free · MIT

Soft, solid, outline, left-accent, glass, neo and full-width banner alerts with tone-picked icons, actions, slide, fade, shake or pop entrances, smooth collapse on dismiss and a pausable auto-dismiss countdown.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/alert.json

The file lands in components/goodui/alert.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/alert works too.

Usage

import { Alert } from "@/components/goodui/alert"
import { Button } from "@/components/goodui/button"

export function PaymentFailed() {
  return (
    <Alert
      tone="danger"
      title="Your last payment failed"
      animation="slide-down"
      dismissible
      actions={
        <>
          <Button size="sm" tone="danger">
            Update card
          </Button>
          <Button size="sm" variant="ghost" tone="neutral">
            View invoice
          </Button>
        </>
      }
    >
      We could not charge the Visa ending in 4242 for $29.00. Online bookings pause in 3 days.
    </Alert>
  )
}

When to use

Use it for
  • A message tied to one page or section: a failed payment, an expiring trial, a sync that needs attention.
  • Form-level errors above a form ("We could not save your changes") with a Retry action.
  • A full-width announcement bar at the top of an app: variant="banner".
  • Short confirmations that go away on their own: autoDismiss with a countdown bar that pauses on hover.
Reach for something else
  • Feedback that floats above the page after an action ("Booking saved"): use toast-stack.
  • An error on one field: use the state and message props of input.
  • A question that needs an answer before continuing: use a dialog such as morphing-dialog.
If you needUse
Inline message in a page or cardvariant="soft" (default) with a tone
Critical problem the user must noticevariant="solid" tone="danger", animation="shake" when it appears after an action
Quiet note inside a dense settings pagevariant="left-accent" or "outline", size="sm"
Site-wide announcement or trial noticevariant="banner" with actions and dismissible
Confirmation that clears itselfautoDismiss (5 s) or autoDismiss={8000}, tone="success"
Over a photo or gradient herovariant="glass"

Examples

Announcement banner

<Alert variant="banner" tone="primary" dismissible actions={<a href="/billing" className="font-semibold underline">Upgrade</a>}>
  Your trial ends in 3 days. Keep online booking and SMS reminders.
</Alert>

Auto-dismissing confirmation

<Alert tone="success" autoDismiss={6000} animation="pop" title="Reminder sent" onDismiss={() => setSent(false)}>
  Maya gets an SMS 24 hours before her 09:30 appointment.
</Alert>

Controlled

const [open, setOpen] = React.useState(true)

<Alert open={open} onOpenChange={setOpen} dismissible tone="warning" title="Calendar sync paused">
  Reconnect Google Calendar to keep availability up to date.
</Alert>

API reference

PropTypeDefault
variant
soft (default), solid, outline, left-accent stripe, frosted glass, neo-brutalist, or banner (full-width bar, centered, ignores size and shape).
"soft" | "solid" | "outline" | "left-accent" | "glass" | "neo" | "banner""soft"
tone
Color and default icon. danger and warning use role="alert", the rest role="status".
"primary" | "neutral" | "success" | "warning" | "danger" | "info""info"
shadow
Drop shadow. Ignored by neo, which has its own offset shadow.
"none" | "sm" | "md" | "lg""none"
animation
Entrance animation, played on mount.
"none" | "slide-down" | "fade" | "shake" | "pop""none"
shape"square" | "rounded" | "pill""rounded"
size"sm" | "md" | "lg""md"
title
Bold first line; children render as the description under it.
React.ReactNode—
icon
Replaces the tone's icon.
React.ReactNode—
hideIcon
Hides the icon entirely.
boolean—
actions
Buttons or links, shown under the text (inline on the banner).
React.ReactNode—
dismissible
Shows a × button.
boolean—
autoDismiss
Close automatically after this many ms (true = 5000), with a countdown bar. Pauses on hover and focus.
boolean | number—
open
Controlled visibility.
boolean—
defaultOpen
Initial visibility when uncontrolled.
booleantrue
onOpenChange
Called with false when the × button or the auto-dismiss timer closes it.
(open: boolean) => void—
onDismiss
Called after the collapse animation finishes.
() => void—
fullWidth
false shrinks the alert to its content.
booleantrue
labels
Text for built-in controls and screen readers; pass translations here.
Partial<AlertLabels>—

Also accepts every <div> attribute.

Accessibility

TabFocusing anything inside the alert (an action or the × button) pauses autoDismiss until focus leaves.
Enter / SpaceActivates the × button (native <button>).
  • tone="danger" and "warning" render role="alert" (announced immediately); other tones render role="status".
  • The tone icon is aria-hidden; the title and text carry the meaning.
  • The × button has aria-label "Dismiss" (labels.dismiss). The countdown bar is aria-hidden.
  • autoDismiss pauses while the pointer is over the alert or focus is inside it, so people can read it and reach its actions.

Motion

  • Entrance on mount: slide-down drops in 10px and fades in 350 ms, fade fades in 350 ms, pop scales from 0.94 with overshoot in 400 ms, shake shakes sideways for 500 ms.
  • Dismiss: fades out in 180 ms while the height collapses over 280 ms (after an 80 ms delay), so content below slides up; onDismiss fires when it finishes.
  • autoDismiss: a 3px bar shrinks linearly over the duration and freezes while paused.
  • Reduced motion: no entrance animation and no transitions, the collapse is instant and the countdown bar is hidden (the timer still runs).

Notes for AI

Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.

  • Default is variant="soft" tone="info". tone picks the icon too (primary Sparkles, neutral Bell, success CircleCheck, warning TriangleAlert, danger CircleAlert, info Info); override with icon or hideIcon.
  • children is the description; title is the bold line. Put buttons in actions, not in children.
  • Uncontrolled alerts hide themselves on dismiss and come back when the string title or text changes. Use open and onOpenChange when the parent decides.
  • autoDismiss={true} means 5000 ms; a number is milliseconds. It needs no dismissible to work.
  • variant="banner" centers its content, shows actions inline and ignores size and shape. labels={{ dismiss: "Schließen" }} translates the × button.

FAQ

What is the difference between onOpenChange and onDismiss?

onOpenChange(false) fires the moment the alert is closed (× or timer). onDismiss fires after the collapse animation, when the alert has left the layout.

Can the banner stick to the top of the page?

Yes. Wrap it in your own sticky container (for example <div className="sticky top-0 z-50">); the alert itself is not positioned.

Related