Alert

A message box that tells people about news, guidance, a finished task, something that needs attention, or an error. It comes in five variants and three types, the same as the Figma set, with an optional action and close button.

Installation

bash
npx @smicolon/fasla-ui@latest add alert

Run init once per project before adding components. See Installation.

Alert is built on Button, so adding it adds button too.

Preview

Maintenance tonight
The dashboard is offline from 22:00 to 23:00.

Variants

variant says what the message is about: default for news, info for guidance, success for a finished task, warning for something that needs attention soon, and destructive for an error. Each variant has its own icon as well as its own colour token, so the meaning never rests on colour alone.

default
New message
You have 12 unread messages in your inbox.
info
Maintenance tonight
The dashboard is offline from 22:00 to 23:00.
success
Payment received
Your March invoice is paid in full.
warning
Storage almost full
You have used 90% of your plan. Delete files or upgrade.
destructive

Types

type sets how strongly the alert stands out. outline, the default, is a card with a 1px edge in the variant's colour. soft lays a 10% tint of that colour over the card. solid fills the alert with the colour and stands out most, so keep it for messages people must not miss. The action follows the type: an outline Button on outline, a solid Button on soft, and a secondary Button on solid.

outline
Payment received
Your March invoice is paid in full.
soft
Payment received
Your March invoice is paid in full.
solid
Payment received
Your March invoice is paid in full.

Icon and avatar

Each variant shows its own 16px icon, centred on the title's first line: an i in a circle for default and info, a check mark for success, a triangle for warning and an exclamation mark in a circle for destructive. Pass another icon to icon to replace it, an Avatar for a message from a person, or set icon to false to leave it out. An avatar is 32px and sits at the top.

Icon
Storage almost full
You have used 90% of your plan. Delete files or upgrade.
Avatar
Layla Haddad replied
“The new layout works. Ship it on Monday.”
No icon
Storage almost full
You have used 90% of your plan. Delete files or upgrade.

Action and close button

AlertAction is a Button at xs, 26px tall, and takes every Button prop, so asChild makes it a link. AlertClose is a ghost Icon Button with a 16px ×. Both sit at the end of the first line, the action first, 12px apart.

BothAction onlyClose only

Long text

The title and the description wrap in their own column. The icon stays on the title's first line, and the action and close button stay at the top. AlertDescription can hold more than a sentence: paragraphs, a list or a link.

Your workspace moves to the new billing plan on 1 April, and some features change

Projects, members and files carry over as they are. Shared links made before the move keep working for 30 days, then ask viewers to sign in.

Read the full list of changes before the move, and tell your team which links to replace.

Closing

Pressing AlertClose hides the alert and calls onOpenChange(false). Control the alert with open to show it again, or to remember that someone closed it. To keep it open, call event.preventDefault() in the close button's onClick.

New message
You have 12 unread messages in your inbox.

Accessibility

  • The role follows the variant. destructive has role="alert": an error needs attention now, so screen readers interrupt what they are reading to announce it. The other variants have role="status", which screen readers announce once they finish the current sentence, because news, guidance, a finished task and a warning can wait that long. Pass role to choose another, such as role="note" for a message that is part of the page from the start.
  • Screen readers announce an alert when it appears. Render it when the event happens, and keep the title and description short, since both are read out.
  • The icon is decorative and hidden from assistive technology; the title says what the message is about.
  • The close button is named “Close”, or “إغلاق” when the nearest lang is Arabic. Pass label to say what it closes.
  • When the alert closes while it holds focus, move focus on in onOpenChange, for example to the control that led to the message. Otherwise the next Tab starts again at the top of the page.
  • In Windows high contrast mode the alert keeps a border in the system's text colour, so its edge stays visible when the fill is dropped.
  • Direction follows the page's dir; there is no prop. Right to left, the row starts on the right with the icon and ends with the close button, and the text is set in Cairo on its taller line.

Props

Alert

PropTypeDefaultDescription
variant"default" | "info" | "success" | "warning" | "destructive""default"What the message is about. Sets the icon, the colour and the default role.
type"outline" | "solid" | "soft""outline"How strongly it stands out: a card with an edge, a tint, or a solid fill.
iconboolean | ReactNodetrueThe variant's icon by default. false leaves it out; any element, such as an icon or an Avatar, replaces it.
openboolean—Whether the alert is shown. Set it to control the alert.
defaultOpenbooleantrueWhether the alert is shown at first, when you don't control it.
onOpenChange(open: boolean) => void—Called with false when the close button is pressed.
rolestring—alert for destructive, status for the rest, unless you pass another.
childrenReactNode—The AlertTitle, AlertDescription, AlertAction and AlertClose, in that order.
classNamestring—Extra classes for the alert. It fills its container's width.

AlertTitle

PropTypeDefaultDescription
childrenReactNode—The title: one short line that says what happened.
classNamestring—Extra classes for the title.

AlertDescription

PropTypeDefaultDescription
childrenReactNode—More detail: a sentence, paragraphs, a list or a link.
classNamestring—Extra classes for the description.

AlertAction

PropTypeDefaultDescription
variant"default" | "outline" | "ghost" | "soft" | "link"—The Button type. Follows the alert's type unless you set it.
tone"primary" | "secondary" | "destructive"—The Button tone. Follows the alert's type unless you set it.
size"xs" | "sm" | "md" | "lg""xs"The Button size.
asChildbooleanfalseMakes its only child the action, with these styles: a link, say.
onClick(event) => void—Called when the action is pressed.
childrenReactNode—The label: a verb for what happens next.
classNamestring—Extra classes for the action. It takes every other Button prop too.

AlertClose

PropTypeDefaultDescription
labelstring—The button's name for screen readers. “Close” by default, in the page's language.
onClick(event) => void—Called before the alert closes. Call event.preventDefault() to keep it open.
childrenReactNode—Another icon in place of the ×.
classNamestring—Extra classes for the close button.

Usage

A message

tsx
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

export function MaintenanceNotice() {
  return (
    <Alert variant="info">
      <AlertTitle>Maintenance tonight</AlertTitle>
      <AlertDescription>The dashboard is offline from 22:00 to 23:00.</AlertDescription>
    </Alert>
  )
}

With an action and a close button

tsx
import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"

export function SaveError({ onRetry }: { onRetry: () => void }) {
  return (
    <Alert variant="destructive" type="soft">
      <AlertTitle>Changes not saved</AlertTitle>
      <AlertDescription>The connection to the server dropped. Check your connection and try again.</AlertDescription>
      <AlertAction onClick={onRetry}>Retry</AlertAction>
      <AlertClose />
    </Alert>
  )
}

Showing a closed alert again

tsx
"use client"

import { useState } from "react"
import { Alert, AlertClose, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function InboxNotice() {
  const [open, setOpen] = useState(true)

  if (!open) {
    return (
      <Button variant="outline" size="sm" onClick={() => setOpen(true)}>
        Show it again
      </Button>
    )
  }

  return (
    <Alert open={open} onOpenChange={setOpen}>
      <AlertTitle>New message</AlertTitle>
      <AlertDescription>You have 12 unread messages in your inbox.</AlertDescription>
      <AlertClose />
    </Alert>
  )
}

A message from a person

tsx
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Avatar } from "@/components/ui/avatar"

export function ReplyNotice() {
  return (
    <Alert icon={<Avatar src="/avatars/layla.png" name="" radius="rounded" />}>
      <AlertTitle>Layla Haddad replied</AlertTitle>
      <AlertDescription>“The new layout works. Ship it on Monday.”</AlertDescription>
      <AlertAction asChild>
        <a href="/messages/42">Reply</a>
      </AlertAction>
    </Alert>
  )
}