Toast
One line rises from the bottom-left; a fermata held over its last word empties while it stays.
Report an issue with ToastExample
Fermata
Changes saved.
Fermata, with an action
Halden archived.
Footnote, two notes
1
Changes saved.
2
Halden archived.
Dateline
Changes saved.
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and button along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/toast.jsonUsage
This example also uses Radio group.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/ radio-group.json"use client"import * as React from "react"import { Button } from "@/components/ui/button"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"import { Toast, Toaster, dismiss, toast, type ToastVariant } from "@/components/ui/toast"const still = () => () => {}export default function Example() {const [variant, setVariant] = React.useState<ToastVariant>("fermata")// The site already mounts a Toaster; this one mounts a commit later, so it's the latest and speaks for the page.const mounted = React.useSyncExternalStore(still, () => true, () => false)return (<div className="grid justify-items-start gap-(--ot-space-7)"><RadioGroup legend="Toasts as" value={variant} onValueChange={(v) => setVariant(v as ToastVariant)}><RadioGroupItem value="fermata">Fermata</RadioGroupItem><RadioGroupItem value="footnote">Footnote</RadioGroupItem><RadioGroupItem value="dateline">Dateline</RadioGroupItem></RadioGroup><div className="flex flex-wrap items-baseline gap-10"><Button onClick={() => toast("Changes saved.")}>Save changes</Button><ButtononClick={() =>toast("Halden archived.", {action: { label: "Undo", onClick: () => toast("Halden restored.") },})}>Archive project</Button><Button variant="quiet" onClick={() => dismiss()}>Clear all</Button></div>{mounted ? <Toaster variant={variant} /> : null}</div>)}// Pinned: a toaster set in place, its clock held as if pointed at.function Still({ variant, children }: { variant: ToastVariant; children: React.ReactNode }) {return (<div className="ot-toaster" data-variant={variant === "fermata" ? undefined : variant} style={{ position: "static", zIndex: "auto", maxInlineSize: "100%" }}>{children}</div>)}Contract
- Underneath: hook: a tiny store and a
<Toaster />. - Anatomy:
.ot-toaster(aria-live="polite", fixed bottom left), holding.ot-toastelements. Each is a sentence,svg.ot-toast-timer(a fermata: arcpathLength="1"plus an accent dot) hung up against its full stop, the way the sign is written over the note it holds, and an optional bracket action. - The fermata's arc empties over
--life. Pointing at the toast or focusing it holds the pause, which is literally what a fermata means. At most three toasts show at once. The toast rises, then its sentence is written in from the left. It leaves withdata-leaving: it sinks and fades, then its height closes so the toasts above settle down into the space. - Yours: the item's name inside the sentence, e.g. "Archived Spring notes."
variantsits on the Toaster (data-variant); each keeps the fermata as its clock, unseen, and pointing still holds the stay.footnote: the book's note at the foot of the page. A short ink rule draws in over the first note, and each note carries its number (.ot-toast-n) hung in the margin so the sentences keep one edge under the rule. Numbers count up while notes stand and start again at 1 on an empty page; only the newest takes the accent, the rest are pencil. One frame holds the whole foot, not one per note.dateline: the frame row of a poster, each toast in its frame holding a sentence, a hairline and the time it happened (time.ot-toast-time, the reader's own clock). The hairline (.ot-toast-rule) runs out toward the time while the toast stays. Right to left, the rule runs out the other way and the sentence writes in from the right. When a page mounts a second<Toaster />, the one mounted last shows the toasts and the others fall silent, so nothing is read out twice.
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
toast(message, opts?) | (message: ReactNode, opts?: { action?: { label: string; onClick: () => void }; duration?: number }) => string | Say one thing. Returns the toast's id. At most three stay on screen; an action keeps its name (Undo). | |
opts.action | { label: string; onClick: () => void } | One bracket button. Choosing it runs onClick and sends the toast away. | |
opts.duration | number | 5000 | How long it stays, in milliseconds. Pointing at the toast, or focusing inside it, holds the pause. |
dismiss(id?) | (id?: string) => void | Send one toast away, or every toast when no id is given. | |
<Toaster /> | component | Render once, near the root. It is a polite live region, so each toast is announced when it arrives. If more than one is mounted, the latest one shows the toasts and the rest stay quiet. | |
<Toaster variant> | "fermata" | "footnote" | "dateline" | "fermata" | fermata: each toast is framed, and a fermata hangs over its last word; the arc empties while it stays. footnote: the notes sit at the foot of the page under a short rule, numbered as they come, the number hung in the margin, the newest in the accent; an empty page starts again at 1. dateline: the sentence and the time it happened on one hairline, which runs out toward the time. In all three, pointing at a toast or focusing inside it holds its stay. |
<Toast /> | component | One toast, as the Toaster draws it. Render it yourself only to show one still, inside a .ot-toaster, as the docs do. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Toast | The fermata sign | The arc over the last word empties; pointing holds it |
| Footnote toast | A book's footnotes | Numbered notes under a short rule |
| Dateline toast | SHAPES / GRADIENTS frame row | The sentence and its time on a hairline that runs out |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Toast | Written | It rises, the sentence writes in, and the stack closes over the gap when it leaves |
| Footnote toast | Spiccato | The rule draws in over the first note; each note rises and its number lands |
| Dateline toast | Held | The hairline runs out toward the time, and holds while pointed at |