Toast
An imperative notification system. Wrap your app (or any subtree) in Toast.Provider, render a single Toast.Viewport somewhere, and call useToast().toast(...) from anywhere inside.
The Provider owns the queue; the Viewport renders each active toast through a render-prop, giving you total control over markup. Auto-dismiss timers are per-toast and pause on hover by default.
Features
useToast() API — fire toasts from any descendant.duration; 0 keeps the toast persistent.status values for success / warning / danger / info styling.id updates in place instead of stacking.Example
Anatomy
import { Toast, useToast } from '@wire-ui/react'
function App() {
return (
<Toast.Provider>
<Trigger />
<Toast.Viewport>
{(t, dismiss) => (
<Toast.Root key={t.id}>
<Toast.Title>{t.title}</Toast.Title>
<Toast.Description>{t.description}</Toast.Description>
<Toast.Close onClick={dismiss}>×</Toast.Close>
</Toast.Root>
)}
</Toast.Viewport>
</Toast.Provider>
)
}
function Trigger() {
const { toast } = useToast()
return <button onClick={() => toast({ title: 'Saved' })}>Save</button>
}useToast hook
useToast() returns { toast, dismiss, toasts }. Must be called from a descendant of Toast.Provider.
toast(input)— adds a toast, returns itsid. If you pass anidand one already exists with that id, it’s replaced in place.dismiss(id)— removes a toast.toasts— the current queue (a reactive value: array in React, computed ref in Vue, signal getter in Solid).
React
import { useToast } from '@wire-ui/react'
function SaveButton() {
const { toast, dismiss } = useToast()
return (
<button onClick={() => {
const id = toast({
title: 'Uploading…',
description: 'design-mock.png',
duration: 0, // persistent until you dismiss
})
setTimeout(() => {
toast({ id, title: 'Uploaded', description: 'design-mock.png', status: 'success', duration: 3000 })
}, 2000)
}}>
Upload
</button>
)
}Styling
The Viewport wraps each toast in a <div role="status" aria-live="polite" data-status="…">. Style based on data-status:
<Toast.Root className="
data-[status=success]:border-black data-[status=success]:bg-[#f5f5f5]
data-[status=warning]:border-black
data-[status=danger]:border-black data-[status=danger]:bg-black data-[status=danger]:text-white
" />Toast.Close is a Button primitive — use [data-hover], [data-focus-visible], [data-active] for interaction styling.
Using data attributes
[data-status="success"] { /* successful action */ }
[data-status="warning"] { /* heads-up */ }
[data-status="danger"] { /* error */ }
[data-status="info"] { /* informational */ }
button[data-hover] { /* close button hover */ }
button[data-focus-visible] { /* close button keyboard focus */ }Provider props
| Prop | Type | Default | Description |
|---|---|---|---|
defaultDuration | number | 5000 | Auto-dismiss timeout (ms) applied to toasts that don’t specify one |
Viewport props
Toast.Viewport takes a render-prop child: (toast: ToastData, dismiss: () => void) => ReactNode. It also accepts standard div props for styling (className, etc).
Toast input (toast(...) argument)
| Field | Type | Default | Description |
|---|---|---|---|
id | string | auto-generated | Stable id; re-firing with the same id replaces in place |
title | ReactNode | — | Toast title |
description | ReactNode | — | Toast description |
status | 'default' | 'success' | 'warning' | 'danger' | 'info' | 'default' | Drives data-status for styling |
duration | number | Provider’s defaultDuration | Auto-dismiss after N ms. 0 or negative = persistent |
pauseOnHover | boolean | true | Pause auto-dismiss while pointer or focus is inside |
data | Record<string, unknown> | — | Arbitrary data passed through to the Viewport render-prop |
Data attributes
| Attribute | Element | Values |
|---|---|---|
data-status | Viewport shell wrapping each toast | "default" / "success" / "warning" / "danger" / "info" |
Accessibility
- Each toast is wrapped in
<div role="status" aria-live="polite">so screen readers announce them as they appear. - The Viewport is
<div role="region" aria-label="Notifications">. Toast.Closeis a real<button>witharia-label="Close notification".- Hovering or focusing a toast pauses its dismiss timer — users can read longer messages.
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | Moves focus into the Viewport and through Close buttons; pauses the timer while focused. |
| Enter / Space | Activates the focused Close button. |
Internals
Since 0.3.0, each toast’s auto-dismiss timer is delegated to useTimeout — pause-on-hover and pause-on-focus reuse the same stop / reset controls. No public API change.