Skip to content
Yuzur1ha Open

Component referenceVersion Draft

Toast

Use one ToastProvider and a shared viewport for independent messages. Simple and rich messages may coexist.

Design notes

Each item controls its own lifetime. duration=false keeps a message until it is dismissed; closing state updates before onExitComplete removes the record.

Design sourceOpen in Figma

Toast design

Inspect the original component, variants and visual details.

Playground

Playgrounddemo.tsx
Display mode
Auto close

Each message keeps its own mode and duration. Persistent messages stay until dismissed. Hover or focus pauses expiry.

    content/components/toast/demo.tsx

    import { useRef, useState } from "react"
    import {
      Button,
      Toast,
      ToastProvider,
      Segmented,
      type ToastTone,
      type ToastDuration,
    } from "@yuzur1ha/ui"
    const toneLabels: Record<ToastTone, string> = {
      info: "Info",
      success: "Success",
      warning: "Warning",
      error: "Error",
    }
    type Item = {
      id: number
      tone: ToastTone
      variant: "simple" | "rich"
      open: boolean
      duration?: ToastDuration
    }
    export function ToastDemo() {
      const [variant, setVariant] = useState<"simple" | "rich">("rich")
      const [lifetime, setLifetime] = useState<"default" | "custom" | "persistent">(
        "default",
      )
      const [result, setResult] = useState("")
      const [items, setItems] = useState<Item[]>([])
      const sequence = useRef(0)
      function add(tone: ToastTone) {
        const item: Item = {
          id: ++sequence.current,
          tone,
          variant,
          open: true,
          ...(lifetime === "default"
            ? {}
            : { duration: lifetime === "persistent" ? false : 10000 }),
        }
        setItems((current) => [...current, item])
      }
      return (
        <>
          <Segmented
            label="Display mode"
            value={variant}
            onChange={setVariant}
            options={[
              { value: "simple", label: "Simple" },
              { value: "rich", label: "Rich" },
            ]}
          />
          <Segmented
            label="Auto close"
            value={lifetime}
            onChange={setLifetime}
            options={[
              { value: "default", label: "Default · 5s" },
              { value: "custom", label: "10 seconds" },
              { value: "persistent", label: "Persistent" },
            ]}
          />
          <div className="demo-row">
            {(["info", "success", "warning", "error"] as const).map((tone) => (
              <Button appearance="secondary" key={tone} onClick={() => add(tone)}>
                {toneLabels[tone]}
              </Button>
            ))}
          </div>
          <p className="demo-label">
            Each message keeps its own mode and duration. Persistent messages stay
            until dismissed. Hover or focus pauses expiry.
          </p>
          <ToastProvider
            label="Notifications"
            viewportLabel="Notifications ({hotkey})"
          >
            {items.map((item) => (
              <Toast
                key={item.id}
                open={item.open}
                {...(item.duration !== undefined
                  ? { duration: item.duration }
                  : {})}
                onOpenChange={(open) =>
                  setItems((current) =>
                    current.map((entry) =>
                      entry.id === item.id ? { ...entry, open } : entry,
                    ),
                  )
                }
                onExitComplete={() =>
                  setItems((current) =>
                    current.filter((entry) => entry.id !== item.id),
                  )
                }
                tone={item.tone}
                toneLabel={toneLabels[item.tone]}
                title={
                  item.tone === "error"
                    ? "Unable to save. Your draft is preserved."
                    : "Changes saved"
                }
                {...(item.variant === "rich"
                  ? {
                      variant: "rich",
                      description: "Your account settings have been updated.",
                      action: {
                        label: "Undo",
                        onClick: () => setResult("Changes undone in this demo."),
                      },
                    }
                  : { variant: "simple" })}
                closeLabel="Close"
              />
            ))}
          </ToastProvider>
          {result && <output className="demo-result">{result}</output>}
        </>
      )
    }
    

    API

    Toast / ToastItem

    A controlled toast; ToastItem is an alias of Toast. Use one ToastProvider per app and remove records in onExitComplete, after setting open=false. Rich and simple variants can share the same viewport.

    ToastProps
    PropertyTypeRequiredDefaultDescription
    openbooleanYes—Controlled visibility. Set false when a close request is received.
    onOpenChange(open: boolean) => voidYes—Called with false for dismissal. Update open first and retain the record until onExitComplete.
    onExitComplete(() => void)No—Called after the closed toast unmounts following its exit animation. Remove its record here, not in onOpenChange.
    variant"rich" | "simple"No"rich"rich supports description/action; simple forbids both and uses an inline icon and close action.
    toneToastToneYes—Semantic palette and icon: info, success, warning or error.
    toneLabelstringYes—Localized accessible tone label; visible in rich headers and announced in simple toasts.
    titlestringYes—Primary toast message.
    descriptionstringNo—Additional text for variant="rich" only. Not accepted by the simple variant.
    action{ label: string; onClick: () => void; }No—Rich-only action label and callback. The action also dismisses the toast.
    durationToastDurationNoInherits ToastProviderAuto-dismiss delay in milliseconds. Omitted inherits the Provider (5000ms by default); false persists until dismissed.
    closeLabelstringYes—Accessible name of the built-in close action.

    No custom imperative methods. Event callbacks are listed as properties.

    ToastProvider

    Provides timing, swipe handling and one shared viewport. Each toast can override duration. Hover, focus and window blur pause auto-dismiss and resume the remaining time.

    ToastProviderProps
    PropertyTypeRequiredDefaultDescription
    durationToastDurationNo5000Default auto-dismiss delay in milliseconds; false makes toasts persistent. Individual toast duration takes precedence.
    viewportLabelstringNo"Notifications ({hotkey})"Accessible label of the shared viewport; overrides its Radix default.
    labelstringNo"Notification"Radix announcement label used with foreground toast announcements.
    swipeDirection("left" | "right" | "up" | "down")No"right"Direction that dismisses an individual toast by swipe.
    swipeThresholdnumberNo50Minimum swipe distance in pixels before dismissal.
    announcerContainerElement | DocumentFragmentNo—Optional portal destination for the toast live announcer.
    childrenReactNodeNo—Application/toast children sharing this provider and its single viewport.

    No custom imperative methods. Event callbacks are listed as properties.