Toast
StableShort past-tense results with at most one action, like “Run archived · Undo”. Read out politely; they wait while hovered or focused.
Call the function from useToast() with a title, an optional line of detail, an icon and one action. Toasts stack at the bottom centre on phones and the bottom right from 640 pixels up, keep the newest three, and leave after 6 seconds, counted only while the pointer and focus are elsewhere. This site wraps every page in ToastProvider; an app needs to do that once, near its root.
Updated View source
Three shapes
import { Button, useToast } from '@rchursin/product/ui'
/** * Three shapes of toast: a title, a title with a line of detail, and a title with one action. * Each says what already happened, in the past tense. Hover or focus one to keep it on screen. */export default function ToastBasic() { const toast = useToast() return ( <div className="flex flex-wrap items-center justify-center gap-3"> <Button variant="secondary" onClick={() => toast({ title: 'Settings saved' })}> Title only </Button> <Button variant="secondary" onClick={() => toast({ title: 'Agent paused', description: 'New runs wait until you resume it.', icon: 'pause' })}> With detail </Button> <Button variant="secondary" onClick={() => toast({ title: 'Run archived', icon: 'archive', action: { label: 'Undo', onClick: () => toast({ title: 'Run restored', icon: 'undo' }) }, }) } > With an action </Button> </div> )}Usage
import { ToastProvider, useToast } from '@rchursin/product/ui'// Once, near the root<ToastProvider><App /></ToastProvider>
// Anywhere insideconst toast = useToast()toast({ title: 'Run archived', icon: 'archive', action: { label: 'Undo', onClick: restore },})Examples
Act, then offer Undo
Archive acts at once instead of asking first; Undo puts the run back in its place. Focus moves to the next row, and back to the run after Undo.
import { useId, useState } from 'react'import { IconButton, useToast } from '@rchursin/product/ui'
const sample = [ { id: 1482, name: 'Nightly eval' }, { id: 1481, name: 'Docs agent · sync' }, { id: 1480, name: 'Release notes draft' },]
/** * Archive acts at once and offers Undo, instead of asking first. Undo puts the run back where it * was. Focus moves to the next row (and back to the run after Undo), so the keyboard never drops * to the top of the page. */export default function ToastUndo() { const toast = useToast() const listId = useId() const [runs, setRuns] = useState(sample)
function archive(index: number) {API
toast(options)
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | string | – | What happened, in the past tense: “Run archived”. |
description | string | – | One line of detail under the title. |
icon | IconName | 'check-square' | Drawn before the text. |
action | { label: string; onClick: () => void } | – | One button, such as Undo or Open. Pressing it runs onClick and dismisses the toast. |
duration | number | 6000 | Milliseconds on screen, counted only while the toast isn’t hovered or focused. |
ToastProvider props
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | Your app. The notifications region renders after it. |
CSS
| Class or variable | What it does |
|---|---|
rc-toast | With motion allowed, each toast fades in and rises 8 pixels on arrival. It leaves at once. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Tab | The notifications region comes last on the page, so Tab reaches a toast’s buttons after everything else. Focus on a toast stops its timer. |
| EnterSpace | On the action: runs it and dismisses the toast. On ×: dismisses it. Either way focus goes back to where it was before it came to the toast. |
- The list is a polite live region (additions only) inside a region named “Notifications” that is always on the page, so each new toast is read once, without interrupting.
- A toast waits while the pointer is over it or focus is inside it; the rest of its time runs once both have left.
- Closing a toast from its own buttons hands focus back to the element that had it before (when it is still on the page), so keyboard focus never drops to the top of the page. An action that moves focus itself, like the Undo example, still wins.
- At most three show at once; a fourth pushes out the oldest.
- The close button is named “Dismiss”. The icon is decorative.
- There is no shortcut to jump to toasts, and they leave on their own, so whatever a toast offers must also be possible somewhere else.
- Without ToastProvider, useToast returns a function that does nothing.
Guidelines
Do
- Say what already happened, in the past tense: “Run archived”.
- Offer at most one action, and make it reversible: Undo, Open.
- Act first and offer Undo, instead of asking “Are you sure?”.
Don’t
- Don’t use a toast for an error that needs fixing; put it next to the field or in an error summary.
- Don’t make a toast the only way to reach something; it is gone in six seconds.
- Don’t toast what the person can already see changing where they are looking.
In use
Used on this site
Related
- Screen-reader announcementsuseAnnounce makes screen readers say a result like “Moved to Done”, politely or, for urgent things, at once. Nothing shows on screen.
- Inline confirmA question asked in place of the buttons, never with window.confirm: “Abandon this session?” with Keep going first.
- MenuA menu button with actions, checkbox and radio items, group labels, shortcut hints and type-ahead, in a popover beside its trigger.
- BannersIn-page messages for things that happen on their own: a timer that finished, a reminder that came due.
Source
The component’s own files, exactly as they run on this site.
/** * Toasts: short, past-tense results with at most one action ("Task moved · Undo"). * Announced politely; they wait while the pointer or focus is on them; 6 seconds otherwise. * * const toast = useToast() * toast({ title: 'Task deleted', action: { label: 'Undo', onClick: restore } }) */import { createContext, useCallback, useContext, useEffect, useRef, useState, type ReactNode,} from "react"import { Icon, type IconName } from "../icons/Icon"
export interface ToastInput { title: string