HextaUI

Toast

De brefs messages qui s’empilent proprement, se déploient au survol, se balayent pour disparaître et transforment un état de chargement en succès ou en erreur sur place.

"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ToastDemo() {
  return (
    <Button
      variant="outline"
      onClick={() =>
        toast("Event created", {
          description: "Sunday, December 3 at 9:00 AM",
          action: { label: "Undo", onClick: () => toast("Event removed") },
        })
      }
    >
      Show toast
    </Button>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/toast.json

Ajoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.

Ajoutez <Toaster /> une seule fois, près de la racine de votre application.

import { Toaster } from "@/components/ui/toast"

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

Appelez ensuite toast depuis n'importe où, y compris en dehors des composants React.

import { toast } from "@/components/ui/toast"

toast("Event created", { description: "Sunday at 9:00 AM" })
toast.success("Payment received")
toast.promise(save(), { loading: "Saving…", success: "Saved", error: "Couldn't save" })

Les toasts s'empilent proprement, le plus récent devant. Survolez ou focalisez la pile pour la déployer ; tous les minuteurs se mettent en pause pendant que vous lisez. Balayez un toast vers son bord pour le fermer.

Types

Tous les toasts utilisent la même surface calme ; seule l'icône porte la couleur.

"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ToastTypes() {
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <Button variant="outline" size="sm" onClick={() => toast("Draft saved")}>
        Default
      </Button>
      <Button
        variant="outline"
        size="sm"
        onClick={() => toast.success("Payment received")}
      >
        Success
      </Button>
      <Button
        variant="outline"
        size="sm"
        onClick={() =>
          toast.error("Couldn't upload photo", {
            description: "The file is larger than 10 MB.",
          })
        }
      >
        Error
      </Button>
      <Button
        variant="outline"
        size="sm"
        onClick={() => toast.warning("Storage almost full")}
      >
        Warning
      </Button>
      <Button
        variant="outline"
        size="sm"
        onClick={() => toast.info("A new version is available")}
      >
        Info
      </Button>
    </div>
  )
}

Promise

toast.promise affiche un spinner, puis se transforme sur place en toast de succès ou d'erreur, l'icône surgissant au changement.

"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

let attempt = 0

function deploy() {
  attempt += 1
  const fails = attempt % 2 === 0
  return new Promise<{ url: string }>((resolve, reject) =>
    setTimeout(
      () =>
        fails
          ? reject(new Error("Build failed"))
          : resolve({ url: "hextaui.com" }),
      1800
    )
  )
}

export function ToastPromise() {
  return (
    <Button
      variant="outline"
      onClick={() =>
        toast.promise(deploy(), {
          loading: "Deploying…",
          success: (data) => ({
            title: "Deployed",
            description: `Live at ${data.url}`,
          }),
          error: (error) => ({
            title: "Deploy failed",
            description: error instanceof Error ? error.message : "Try again.",
          }),
        })
      }
    >
      Deploy (fails every other time)
    </Button>
  )
}

Action

Passez action pour un bouton comme Annuler. Un clic dessus ferme aussi le toast.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ToastAction() {
  const [archived, setArchived] = React.useState(0)

  return (
    <div className="flex flex-col items-center gap-3">
      <Button
        variant="outline"
        onClick={() => {
          setArchived((count) => count + 1)
          toast("Conversation archived", {
            action: {
              label: "Undo",
              onClick: () => setArchived((count) => count - 1),
            },
          })
        }}
      >
        Archive
      </Button>
      <p className="text-sm text-muted-foreground tabular-nums">
        {archived} archived
      </p>
    </div>
  )
}

Mise à jour sur place

Conservez l'id de toast.loading et modifiez-le avec toast.update, y compris son type.

"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ToastUpdate() {
  return (
    <Button
      variant="outline"
      onClick={() => {
        const id = toast.loading("Uploading 3 files…")
        setTimeout(() => toast.update(id, { title: "Uploading 2 of 3…" }), 900)
        setTimeout(
          () =>
            toast.update(id, {
              type: "success",
              title: "Upload complete",
              description: "3 files added to Photos.",
            }),
          1800
        )
      }}
    >
      Upload files
    </Button>
  )
}

Pas de doublons

Donnez un id à un toast : l'appeler de nouveau met à jour l'existant au lieu d'empiler une copie.

"use client"

import { Button } from "@/components/ui/button"
import { toast } from "@/components/ui/toast"

export function ToastDedupe() {
  return (
    <Button
      variant="outline"
      onClick={() =>
        toast.info("You're offline", {
          id: "offline",
          description: "Changes will sync when you reconnect.",
        })
      }
    >
      Go offline (click twice)
    </Button>
  )
}
ToucheAction
F6Place le focus dans les toasts et les déploie en éventail.
TabSe déplace entre les actions des toasts et les boutons de fermeture.
EscFerme le toast qui a le focus.
  • Les toasts sont annoncés poliment ; toast.error est annoncé immédiatement.
  • Les minuteurs se mettent en pause tant que le pointeur ou le focus est sur les toasts, personne n'a donc à courir contre un compte à rebours.
  • Les boutons de fermeture apparaissent au survol et au focus, et sont toujours visibles sur écran tactile.
  • Avec la réduction des animations, les toasts apparaissent en fondu au lieu de glisser.
PropTypePar défaut
positionLes téléphones utilisent toujours toute la largeur.
"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right""bottom-right"
limitToasts affichés en même temps ; les plus anciens s'estompent.
number3
timeoutDurée d'affichage par défaut, en ms.
number5000
AttributDescription
data-slot="toaster"Le viewport, avec data-position.
data-slot="toast"Chaque toast, avec data-type, data-expanded et data-swiping.
PropTypePar défaut
description
ReactNode–
action
{ label: ReactNode; onClick?: (event) => void }–
idÀ réutiliser pour mettre à jour au lieu de dupliquer.
string–
timeoutReprend par défaut le délai du Toaster. Les longs messages restent plus longtemps, à environ 250 mots par minute.
number–
priority
"low" | "high""low"
onClose
() => void–
onRemove
() => void–

Aussi toast.success, toast.error, toast.warning, toast.info, toast.loading, toast.promise, toast.update et toast.dismiss. Chacun renvoie ou prend l'id du toast.

Utilisé dans les blocks

Des blocks qui s’appuient sur Toast.