HextaUI

Alert

Inline-Meldungen für Status und Feedback, mit neutraler Fläche, farbigem Icon und einem Schließen, das sanft zusammenklappt.

Heads up
You can add components to your app using the CLI.
import { IconCircleX, IconTerminal2 } from "@tabler/icons-react"

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

export function AlertDemo() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert>
        <IconTerminal2 />
        <AlertTitle>Heads up</AlertTitle>
        <AlertDescription>
          You can add components to your app using the CLI.
        </AlertDescription>
      </Alert>
      <Alert variant="destructive">
        <IconCircleX />
        <AlertTitle>Payment failed</AlertTitle>
        <AlertDescription>
          Your card was declined. Update your billing details.
        </AlertDescription>
      </Alert>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/alert.json

Fügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.

import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
<Alert>
  <IconTerminal2 />
  <AlertTitle>Heads up</AlertTitle>
  <AlertDescription>
    You can add components to your app using the CLI.
  </AlertDescription>
</Alert>
Alert
├── AlertTitle
├── AlertDescription
├── AlertAction
└── AlertClose

Varianten

variant färbt nur das Icon, sodass die Fläche neutral bleibt und der Status auf einen Blick lesbar ist, ohne zu schreien.

import {
  IconAlertTriangle,
  IconCircleCheck,
  IconCircleX,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

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

const alerts = [
  {
    variant: "default",
    icon: IconTerminal2,
    title: "Heads up",
    description: "You can add components to your app using the CLI.",
  },
  {
    variant: "destructive",
    icon: IconCircleX,
    title: "Payment failed",
    description: "Your card was declined. Update your billing details.",
  },
  {
    variant: "success",
    icon: IconCircleCheck,
    title: "Changes saved",
    description: "Your profile is now visible to your team.",
  },
  {
    variant: "info",
    icon: IconInfoCircle,
    title: "New version available",
    description: "Reload the page to get the latest features.",
  },
  {
    variant: "warning",
    icon: IconAlertTriangle,
    title: "Trial ends in 3 days",
    description: "Add a payment method to keep your workspace active.",
  },
] as const

export function AlertVariants() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      {alerts.map(({ variant, icon: Icon, title, description }) => (
        <Alert key={variant} variant={variant}>
          <Icon />
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </Alert>
      ))}
    </div>
  )
}

Soft

appearance="soft" entfernt den Rahmen und tönt die ganze Fläche in der Farbe der Variante.

import {
  IconAlertTriangle,
  IconCircleCheck,
  IconCircleX,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

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

const alerts = [
  {
    variant: "default",
    icon: IconTerminal2,
    title: "Heads up",
    description: "You can add components to your app using the CLI.",
  },
  {
    variant: "destructive",
    icon: IconCircleX,
    title: "Payment failed",
    description: "Your card was declined. Update your billing details.",
  },
  {
    variant: "success",
    icon: IconCircleCheck,
    title: "Changes saved",
    description: "Your profile is now visible to your team.",
  },
  {
    variant: "info",
    icon: IconInfoCircle,
    title: "New version available",
    description: "Reload the page to get the latest features.",
  },
  {
    variant: "warning",
    icon: IconAlertTriangle,
    title: "Trial ends in 3 days",
    description: "Add a payment method to keep your workspace active.",
  },
] as const

export function AlertSoft() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      {alerts.map(({ variant, icon: Icon, title, description }) => (
        <Alert key={variant} variant={variant} appearance="soft">
          <Icon />
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </Alert>
      ))}
      <Alert variant="success" appearance="soft">
        <IconCircleCheck />
        <AlertTitle>Message archived</AlertTitle>
        <AlertAction>
          <Button variant="ghost" size="xs">
            Undo
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
    </div>
  )
}

Teilweiser Inhalt

Nur Titel, nur Beschreibung und kein Icon richten sich alle ohne zusätzliche Klassen aus. Das Grid platziert jeden Teil in seiner eigenen Spalte.

import { IconCircleCheck, IconInfoCircle } from "@tabler/icons-react"

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

export function AlertPartialContent() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert variant="success">
        <IconCircleCheck />
        <AlertTitle>Invite sent</AlertTitle>
        <AlertClose />
      </Alert>
      <Alert>
        <IconInfoCircle />
        <AlertDescription>
          Sessions expire after 30 days of inactivity.
        </AlertDescription>
      </Alert>
      <Alert>
        <AlertTitle>No icon</AlertTitle>
        <AlertDescription>The content starts at the padding.</AlertDescription>
      </Alert>
    </div>
  )
}

Aktionen

Lege Buttons in <AlertAction />. Sie richten sich an der ersten Zeile des Titels aus, auch wenn dieser umbricht.

import { IconInfoCircle, IconTerminal2 } from "@tabler/icons-react"

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

export function AlertActions() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert variant="info">
        <IconInfoCircle />
        <AlertTitle>
          A new version of the dashboard is available with faster charts, saved
          filters and a redesigned sidebar
        </AlertTitle>
        <AlertDescription>
          Reloading keeps your current filters.
        </AlertDescription>
        <AlertAction>
          <Button size="xs">Reload</Button>
        </AlertAction>
      </Alert>
      <Alert>
        <IconTerminal2 />
        <AlertTitle>Message archived</AlertTitle>
        <AlertAction>
          <Button variant="secondary" size="xs">
            Undo
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
    </div>
  )
}

Schließbar

Füge <AlertClose /> hinzu, damit Nutzer ihn schließen können. Der Alert klappt sanft ein, und der Inhalt darunter rutscht nach oben. Schließt du ihn per Tastatur, springt der Fokus zum nächsten Bedienelement, hier „Reset“.

"use client"

import * as React from "react"
import { IconInfoCircle } from "@tabler/icons-react"

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

export function AlertDismissible() {
  const [key, setKey] = React.useState(0)

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert key={key} variant="info">
        <IconInfoCircle />
        <AlertTitle>We’ve updated our terms</AlertTitle>
        <AlertDescription>
          Read the <a href="#">new terms</a> before your next billing date.
        </AlertDescription>
        <AlertClose />
      </Alert>
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => setKey(key + 1)}>
          Reset
        </Button>
        <Button variant="outline" size="sm">
          Next focusable
        </Button>
      </div>
    </div>
  )
}

Kontrolliert

Übergib open und onOpenChange, um ihn aus deinem eigenen State zu steuern. Er animiert sowohl beim Einblenden als auch beim Ausblenden.

"use client"

import * as React from "react"
import { IconCircleCheck } from "@tabler/icons-react"

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

export function AlertControlled() {
  const [open, setOpen] = React.useState(true)

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        onClick={() => setOpen(!open)}
      >
        {open ? "Hide" : "Show"} alert
      </Button>
      <Alert variant="success" open={open} onOpenChange={setOpen}>
        <IconCircleCheck />
        <AlertTitle>Deployment complete</AlertTitle>
        <AlertDescription>
          Your changes are live on production.
        </AlertDescription>
        <AlertClose />
      </Alert>
      <p className="text-sm text-muted-foreground">Content below slides up.</p>
    </div>
  )
}

Schließen abbrechen

Rufe event.preventDefault() im onClick des Schließen-Buttons auf, um den Alert offen zu halten, zum Beispiel um zuerst eine Bestätigung einzuholen.

"use client"

import * as React from "react"
import { IconAlertTriangle } from "@tabler/icons-react"

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

export function AlertCancelDismiss() {
  const [confirmed, setConfirmed] = React.useState(false)

  return (
    <Alert variant="warning" className="max-w-lg">
      <IconAlertTriangle />
      <AlertTitle>Unsaved changes</AlertTitle>
      <AlertDescription>
        {confirmed
          ? "Click dismiss again to close."
          : "The first click is cancelled with event.preventDefault()."}
      </AlertDescription>
      <AlertClose
        onClick={(event) => {
          if (!confirmed) {
            event.preventDefault()
            setConfirmed(true)
          }
        }}
      />
    </Alert>
  )
}

Nach dem Laden eingefügt

Alerts, die nach dem Laden der Seite gemountet werden, blenden ein und gleiten herein, und Screenreader sagen sie an. Alerts, die bereits im Server-HTML stehen, erscheinen ohne Bewegung.

"use client"

import * as React from "react"
import { IconCircleX } from "@tabler/icons-react"

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

export function AlertInserted() {
  const [errors, setErrors] = React.useState<number[]>([])

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        onClick={() => setErrors([...errors, Date.now()])}
      >
        Submit form
      </Button>
      {errors.map((id) => (
        <Alert
          key={id}
          variant="destructive"
          onOpenChange={() =>
            setErrors((current) => current.filter((error) => error !== id))
          }
        >
          <IconCircleX />
          <AlertTitle>Email is already in use</AlertTitle>
          <AlertDescription>Try signing in instead.</AlertDescription>
          <AlertClose />
        </Alert>
      ))}
    </div>
  )
}

Umfangreicher Inhalt

Inline-Icons und Links in der Beschreibung behalten ihren eigenen Stil. Nutze render, um dem Titel eine Überschriftenebene zu geben.

import {
  IconExternalLink,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

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

export function AlertRichContent() {
  return (
    <Alert className="max-w-lg">
      <IconInfoCircle />
      <AlertTitle render={<h3 />}>Rendered as an h3</AlertTitle>
      <AlertDescription>
        <p>
          Press <IconTerminal2 className="inline size-4 align-text-bottom" /> to
          open the terminal, or read the <a href="#">docs</a>.
        </p>
        <Button
          variant="outline"
          size="sm"
          nativeButton={false}
          render={<a href="#" />}
        >
          Open guide <IconExternalLink />
        </Button>
      </AlertDescription>
    </Alert>
  )
}

Rechts nach links

Icon, Inhalt und Schließen-Button werden mit der Leserichtung gespiegelt.

import { IconAlertTriangle } from "@tabler/icons-react"

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

export function AlertRtl() {
  return (
    <div dir="rtl" className="w-full max-w-lg">
      <Alert variant="warning">
        <IconAlertTriangle />
        <AlertTitle>تنتهي الفترة التجريبية خلال 3 أيام</AlertTitle>
        <AlertDescription>
          أضف طريقة دفع لإبقاء مساحة العمل نشطة.
        </AlertDescription>
        <AlertClose />
      </Alert>
    </div>
  )
}
TasteAktion
TabSetzt den Fokus auf Links, Aktionen und den Schließen-Button im Alert.
EnterSpaceAktiviert den fokussierten Schließen-Button. Sobald der Alert eingeklappt ist, wandert der Fokus zum nächsten fokussierbaren Element oder zum vorherigen, wenn keines folgt.
  • destructive- und warning-Alerts erhalten role="alert" und werden sofort angesagt. Jede andere Variante erhält role="status" und wartet auf eine Pause.
  • Der Schließen-Button trägt das Label „Dismiss“. Übergib aria-label, um es zu übersetzen.
  • Der Fokus geht nie verloren, wenn ein fokussierter Alert geschlossen wird.
  • Bei reduzierter Bewegung erscheinen und verschwinden Alerts sofort.

Jeder Teil akzeptiert die Attribute des Elements, das er rendert. Die Varianten werden als alertVariants exportiert, um andere Elemente auf die gleiche Weise zu stylen.

PropTypStandard
variantFärbt das Icon ein. destructive und warning werden mit assertive-Priorität angesagt.
"default" | "destructive" | "success" | "info" | "warning""default"
appearance
"outline" | "soft""outline"
open
boolean–
defaultOpen
booleantrue
onOpenChangeWird aufgerufen, wenn der Schließen-Button gedrückt wird.
(open: boolean) => void–
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="alert"Den Alert in CSS ansprechen.
data-variantDie aktuelle Variante.
data-appearanceDas aktuelle Erscheinungsbild.
data-ending-styleVorhanden, während der Alert nach dem Schließen einklappt.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="alert-title"Titel in CSS ansprechen.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="alert-description"Beschreibungen in CSS ansprechen. Einfache Links darin werden automatisch unterstrichen.
PropTypStandard
render
ReactElement | (props, state) => ReactElement<div>
AttributBeschreibung
data-slot="alert-action"Den Aktionsbereich in CSS ansprechen.

Eine Ghost-<Button />, die den nächstgelegenen Alert schließt. Sie akzeptiert alle Button-Props und muss innerhalb von <Alert /> verwendet werden.

PropTypStandard
aria-label
string"Dismiss"
children
ReactNode<IconX />
onClickRufe event.preventDefault() auf, um den Alert offen zu halten.
(event: MouseEvent) => void–
AttributBeschreibung
data-slot="alert-close"Den Schließen-Button in CSS ansprechen.

In Blocks verwendet

Blocks, die auf Alert aufbauen.