Skip to main contentSkip to docs navigation

An alert dialog that interrupts the current flow until the user makes an explicit choice, such as confirming a destructive action.

Looking for a status banner? Chassis calls inline status messages Notifications. An Alert is chassis-css's alert dialog: it blocks the page until the user answers.

Overview

<Alert> renders a <dialog> with role="alertdialog" and opens it as a modal dialog, the way Modal does. Use it for a decision the flow can't continue without: confirming a destructive action, acknowledging an error. For an overlay that doesn't demand a decision, use Modal; for a message that doesn't interrupt, use Notification.

An alert waits for an explicit choice, so by default neither a click on the backdrop nor the Escape key closes it; the alert bounces instead. Set backdrop and keyboard to allow them.

Basic usage

Compose an <AlertIcon> (optional), an <AlertBody> with an <AlertTitle> and an <AlertText>, and an <AlertFooter> with the actions. The footer lays the actions out right to left, so write the primary action first. <AlertCancel> is the least destructive action: it closes the alert, and it has focus when the alert opens.

Delete the file?

This file will be removed for everyone. This can't be undone.

JSX
import { useState } from 'react'
import {
  Alert,
  AlertBody,
  AlertCancel,
  AlertFooter,
  AlertIcon,
  AlertText,
  AlertTitle,
  Button
} from '@chassis-ui/react'

export const Example = () => {
  const [visible, setVisible] = useState(false)
  return (
    <>
      <Button color="danger" onClick={() => setVisible(true)}>
        Delete file
      </Button>
      <Alert visible={visible} onVisibleChange={setVisible}>
        <AlertIcon name="exclamation-triangle-solid" color="danger" />
        <AlertBody>
          <AlertTitle>Delete the file?</AlertTitle>
          <AlertText>This file will be removed for everyone. This can't be undone.</AlertText>
        </AlertBody>
        <AlertFooter>
          <Button color="danger" onClick={() => setVisible(false)}>
            Delete file
          </Button>
          <AlertCancel>Cancel</AlertCancel>
        </AlertFooter>
      </Alert>
    </>
  )
}

Error codes

<AlertCode> shows a machine-readable reference under the title, in subdued type.

Upload failed

ERR-1234

The server refused the file. Try again, or contact support with the code above.

JSX
import { useState } from 'react'
import {
  Alert,
  AlertBody,
  AlertCancel,
  AlertCode,
  AlertFooter,
  AlertIcon,
  AlertText,
  AlertTitle,
  Button
} from '@chassis-ui/react'

export const Example = () => {
  const [visible, setVisible] = useState(false)
  return (
    <>
      <Button onClick={() => setVisible(true)}>Upload</Button>
      <Alert visible={visible} onVisibleChange={setVisible}>
        <AlertIcon name="exclamation-circle-solid" color="danger" />
        <AlertBody>
          <AlertTitle>Upload failed</AlertTitle>
          <AlertCode>ERR-1234</AlertCode>
          <AlertText>
            The server refused the file. Try again, or contact support with the code above.
          </AlertText>
        </AlertBody>
        <AlertFooter>
          <AlertCancel>Close</AlertCancel>
        </AlertFooter>
      </Alert>
    </>
  )
}

Close button

An alert's actions are usually its only way out. When it needs one without an action of its own, closeButton adds a close button in the corner.

Your session is about to expire

Renew it to keep working, or close this and be signed out in 2 minutes.

JSX
import { useState } from 'react'
import { Alert, AlertBody, AlertFooter, AlertText, AlertTitle, Button } from '@chassis-ui/react'

export const Example = () => {
  const [visible, setVisible] = useState(false)
  return (
    <>
      <Button onClick={() => setVisible(true)}>Show alert</Button>
      <Alert closeButton closeLabel="Close alert" visible={visible} onVisibleChange={setVisible}>
        <AlertBody>
          <AlertTitle>Your session is about to expire</AlertTitle>
          <AlertText>
            Renew it to keep working, or close this and be signed out in 2 minutes.
          </AlertText>
        </AlertBody>
        <AlertFooter>
          <Button color="primary" onClick={() => setVisible(false)}>
            Renew session
          </Button>
        </AlertFooter>
      </Alert>
    </>
  )
}

On small screens the actions share one row. stacked stacks them as full-width rows instead. Push a separate action, such as a destructive one, to the other end with className="sm:me-auto".

Save changes?

Do you want to save your changes before leaving the page?

JSX
import { useState } from 'react'
import {
  Alert,
  AlertBody,
  AlertCancel,
  AlertFooter,
  AlertIcon,
  AlertText,
  AlertTitle,
  Button
} from '@chassis-ui/react'

export const Example = () => {
  const [visible, setVisible] = useState(false)
  return (
    <>
      <Button onClick={() => setVisible(true)}>Leave page</Button>
      <Alert visible={visible} onVisibleChange={setVisible}>
        <AlertIcon name="info-circle-solid" />
        <AlertBody>
          <AlertTitle>Save changes?</AlertTitle>
          <AlertText>Do you want to save your changes before leaving the page?</AlertText>
        </AlertBody>
        <AlertFooter stacked>
          <Button color="primary" onClick={() => setVisible(false)}>
            Save changes
          </Button>
          <AlertCancel>Cancel</AlertCancel>
          <Button
            className="sm:me-auto"
            color="danger"
            variant="outline"
            onClick={() => setVisible(false)}
          >
            Discard
          </Button>
        </AlertFooter>
      </Alert>
    </>
  )
}

Sequential alerts

One alert can lead to another: open the next from an action of the current one. The second alert below asks for a typed confirmation, and its field has focus when it opens, because it carries data-autofocus.

Delete your account?

All of your data will be removed.

Final confirmation

This is permanent. Type DELETE to confirm.

JSX
import { useState } from 'react'
import {
  Alert,
  AlertBody,
  AlertCancel,
  AlertFooter,
  AlertIcon,
  AlertText,
  AlertTitle,
  Button,
  TextInput
} from '@chassis-ui/react'

export const Example = () => {
  const [step, setStep] = useState<'none' | 'ask' | 'confirm'>('none')
  const [typed, setTyped] = useState('')
  const close = () => {
    setStep('none')
    setTyped('')
  }
  return (
    <>
      <Button color="danger" onClick={() => setStep('ask')}>
        Delete account
      </Button>
      <Alert visible={step === 'ask'} onClose={close}>
        <AlertIcon name="exclamation-triangle-solid" color="danger" />
        <AlertBody>
          <AlertTitle>Delete your account?</AlertTitle>
          <AlertText>All of your data will be removed.</AlertText>
        </AlertBody>
        <AlertFooter>
          <Button color="danger" onClick={() => setStep('confirm')}>
            Delete account
          </Button>
          <AlertCancel>Cancel</AlertCancel>
        </AlertFooter>
      </Alert>
      <Alert visible={step === 'confirm'} onClose={close}>
        <AlertIcon name="exclamation-triangle-solid" color="danger" />
        <AlertBody>
          <AlertTitle>Final confirmation</AlertTitle>
          <AlertText>
            This is permanent. Type <strong>DELETE</strong> to confirm.
          </AlertText>
          <TextInput
            aria-label="Type DELETE to confirm"
            data-autofocus=""
            value={typed}
            onChange={setTyped}
          />
        </AlertBody>
        <AlertFooter>
          <Button color="danger" disabled={typed !== 'DELETE'} onClick={close}>
            Confirm delete
          </Button>
          <AlertCancel>Cancel</AlertCancel>
          <Button className="sm:me-auto" variant="link" onClick={() => setStep('ask')}>
            Go back
          </Button>
        </AlertFooter>
      </Alert>
    </>
  )
}

Accessibility

<Alert> names itself with its <AlertTitle> (aria-labelledby) and describes itself with its <AlertCode> and <AlertText> (aria-describedby), the two an alertdialog needs. Each part is listed under the id it has on the page (your own id, or the child's under asChild), in page order, and only while it is rendered. So both attributes are added once the alert has mounted, never in the server's HTML. Set your own aria-labelledby or aria-describedby to point elsewhere.

When the alert opens, focus moves to its first element with data-autofocus: <AlertCancel> has it, so the least destructive action is focused, as the WAI-ARIA pattern recommends. Give an earlier element data-autofocus to focus it instead, or opt <AlertCancel> out with data-autofocus="false". React's autoFocus isn't enough: in a page rendered in the browser, React writes no autofocus attribute for the dialog to find.

When the alert closes, focus returns to the element that had it when the alert opened. After a chain of alerts, that is the element that opened the first one.

API

Alert

PropTypeDefault
backdrop?true | "static"static
Whether a click on the backdrop closes the alert. By default it doesn't ('static'): the alert bounces, since an alert dialog waits for an explicit choice. Set true to let it close.
className?string—
A string of all className you want applied to the base component.
closeButton?boolean—
Adds a close button in the corner, for an alert that needs a way out besides its actions.
closeLabel?string—
The close button's accessible name. Defaults to 'Close'; set it for non-English UIs.
defaultVisible?boolean—
Whether the alert is open when it first renders. Such an alert is uncontrolled: it closes itself on a close request. Use visible to open and close it from outside.
instant?boolean—
Disable the open/close transition entirely.
keyboard?booleanfalse
Whether the Escape key closes the alert. By default it doesn't: the alert bounces instead.
onClose?(() => void)—
Callback fired when the alert asks to be closed: AlertCancel, the close button, and, when allowed, Escape or a backdrop click.
onClosePrevented?(() => void)—
Callback fired when a close attempt is blocked: a click on a static backdrop, or Escape while keyboard is off.
onHidden?(() => void)—
Callback fired after the exit transition completes and the alert is fully hidden.
onShow?(() => void)—
Callback fired when the alert starts to open.
onShown?(() => void)—
Callback fired after the entry transition completes and the alert is fully visible.
onVisibleChange?((visible: boolean) => void)—
Callback fired with false when the alert asks to be closed, at the same moments as onClose. It makes visible and onVisibleChange a pair that takes a state setter.
open?boolean—
Renders the alert open from its first paint, server HTML included, as a non-modal dialog: a static preview. It stays open until a close request. Use visible or defaultVisible to open it as a modal dialog.
visible?boolean—
Whether the alert is open. The alert is then controlled: a close request only fires onClose and onVisibleChange, and the alert closes when this becomes false.

AlertIcon

PropTypeDefault
className?string—
A string of all className you want applied to the component.
font?boolean—
namestring—
Icon name, e.g. folder-tree. Matches an id in the SVG sprite, or a cx-{name} font glyph class in font mode (see IconProvider's fontPrefix).
size?string | number—
Icon size: a number of pixels, or any CSS length ('1.25rem', '1em'). Sets chassis-css's --cx-icon-size on the icon, so it applies in font mode too. Leave unset to keep the size chassis-css gives icons in context (inside a button, an input, a navbar toggler, ...).
sprite?string—
URL of the SVG sprite file. Defaults to IconProvider's sprite, or to none — a sprite embedded in the page, referenced as #name. Ignored in font mode.
title?string—
Accessible name. When set, the icon is exposed to assistive tech instead of hidden.

AlertBody

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType'div'
Component used for the root node. Either a string to use an HTML element or a component.
ref?any—

AlertTitle

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType'h2'
Component used for the root node. Either a string to use an HTML element or a component.
ref?any—

AlertCode

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType'code'
Component used for the root node. Either a string to use an HTML element or a component.
ref?any—

AlertText

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType'p'
Component used for the root node. Either a string to use an HTML element or a component.
ref?any—

AlertFooter

PropTypeDefault
asChild?boolean—
Render the single child element in place of this component's own element, with this component's classes, props and ref merged onto it: <Button asChild><Link href="/login">Log in</Link></Button>. The alternative to component for React Server Components — an element can be passed from the server to this package's client components, a component reference (component={Link}) can't.
className?string—
A string of all className you want applied to the component.
component?ElementType'div'
Component used for the root node. Either a string to use an HTML element or a component.
ref?any—
stacked?boolean—
Stacks the actions as full-width rows on small screens, instead of sharing one row.

AlertCancel

PropTypeDefault
className?string—
A string of all className you want applied to the base component.
color?ContextColordefault
Sets the color of the component to one of Chassis context colors.
disabled?boolean—
Toggle the disabled state for the component.
onClick?MouseEventHandler<HTMLButtonElement | HTMLAnchorElement | HTMLInputElement>—
Fires on click. Typed for every element component can actually render (button, a, input, or a custom component), rather than narrowed to HTMLButtonElement alone.
pressed?boolean—
Marks the button as pressed for toggle-style usage (e.g. a formatting toolbar button). Applies the .active class and sets aria-pressed so assistive technology announces "button, pressed" rather than treating the button as a navigation link. A link (href, or an <a> as component or the asChild element) has no pressed state to announce: it gets the class alone, unless you give it role="button".
shape?Shapes—
Select the shape of the component.
size?"sm" | "lg"—
Size the component sm or lg.
type?"button" | "submit" | "reset"—
Specifies the type of button. Always specify the type attribute for the <button> element. Different browsers may use different default types for the <button> element.
variant?"link" | "basic" | "outline" | "smooth"—
Set the button style variant. Same as ContextStyle, but solid (the unmodified default look) doesn't apply as a class, and link — button-specific, not a context color — makes the button look and behave like a hyperlink while keeping its color.