HextaUI

Alert

Mensagens inline para status e feedback, com uma superfície neutra, um ícone colorido e um botão de dispensar que recolhe suavemente.

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

Adiciona o componente, os tokens de tema do HextaUI e quaisquer componentes do HextaUI dos quais ele depende.

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

Variantes

variant colore apenas o ícone, então a superfície permanece neutra e o status é lido de relance sem chamar atenção demais.

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>
  )
}

Suave

appearance="soft" remove a borda e tinge toda a superfície com a cor da variant.

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>
  )
}

Conteúdo parcial

Só título, só descrição e sem ícone ficam todos alinhados sem classes extras. O grid coloca cada parte em sua própria coluna.

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>
  )
}

Ações

Coloque botões em <AlertAction />. Eles se alinham à primeira linha do título, mesmo quando ele quebra de linha.

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>
  )
}

Dispensável

Adicione <AlertClose /> para que as pessoas possam dispensá-lo. O alerta colapsa suavemente e o conteúdo abaixo sobe. Dispense-o pelo teclado e o foco vai para o próximo controle, aqui “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>
  )
}

Controlado

Passe open e onOpenChange para controlá-lo com seu próprio estado. Ele anima tanto a entrada quanto a saída.

"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>
  )
}

Cancelar um fechamento

Chame event.preventDefault() no onClick do botão de fechar para manter o alerta aberto, por exemplo para pedir confirmação antes.

"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>
  )
}

Inserido após o carregamento

Alertas montados depois do carregamento da página aparecem com fade e deslize, e os leitores de tela os anunciam. Alertas que já estão no HTML do servidor aparecem sem movimento.

"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>
  )
}

Conteúdo rico

Ícones inline e links dentro da descrição mantêm o próprio estilo. Use render para dar um nível de título ao title.

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>
  )
}

Da direita para a esquerda

O ícone, o conteúdo e o botão de fechar são espelhados conforme a direção de leitura.

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>
  )
}
TeclaAção
TabMove o foco para links, ações e o botão de fechar dentro do alerta.
EnterSpaceAtiva o botão de fechar em foco. Depois que o alerta colapsa, o foco vai para o próximo elemento focável, ou para o anterior quando não há nenhum depois.
  • Alertas destructive e warning recebem role="alert" e são anunciados imediatamente. Todas as outras variants recebem role="status" e esperam uma pausa.
  • O botão de fechar tem o rótulo “Dismiss”. Passe aria-label para traduzi-lo.
  • O foco nunca se perde quando um alerta em foco é fechado.
  • Com movimento reduzido ativado, os alertas aparecem e desaparecem instantaneamente.

Cada parte aceita os atributos do elemento que renderiza. As variants são exportadas como alertVariants para estilizar outros elementos da mesma forma.

PropTipoPadrão
variantColore o ícone. destructive e warning são anunciados de forma assertiva.
"default" | "destructive" | "success" | "info" | "warning""default"
appearance
"outline" | "soft""outline"
open
boolean–
defaultOpen
booleantrue
onOpenChangeChamado quando o botão de fechar é pressionado.
(open: boolean) => void–
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="alert"Seleciona o alerta no CSS.
data-variantA variante atual.
data-appearanceA aparência atual.
data-ending-stylePresente enquanto o alerta colapsa após ser dispensado.
PropTipoPadrão
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="alert-title"Seleciona os títulos no CSS.
PropTipoPadrão
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="alert-description"Seleciona as descrições no CSS. Links simples dentro delas são sublinhados para você.
PropTipoPadrão
render
ReactElement | (props, state) => ReactElement<div>
AtributoDescrição
data-slot="alert-action"Seleciona a área de ações no CSS.

Um <Button /> ghost que fecha o alerta mais próximo. Aceita todas as props de Button e deve ser usado dentro de <Alert />.

PropTipoPadrão
aria-label
string"Dismiss"
children
ReactNode<IconX />
onClickChame event.preventDefault() para manter o alerta aberto.
(event: MouseEvent) => void–
AtributoDescrição
data-slot="alert-close"Seleciona o botão de fechar no CSS.

Usado em blocos

Blocos que se baseiam em Alert.