Skip to content

Toast

A short notification about something that just happened: a save, a send, a failure. It shows in the page’s Toaster, which announces it, times it and dismisses it.

import { Toast } from '@zitdevs/ui'; // a toast the server renders
import { toast } from '@zitdevs/ui/enhance'; // a toast raised from anywhere

Each button calls toast() with its tone. This page renders a Toaster and enhances it with enhanceToaster(document), as a site without React does. The danger toast stays until you dismiss it; the others leave after five seconds, and wait while the pointer or the focus is on them.

import { toast } from '@zitdevs/ui/enhance';
const id = toast({
tone: 'success',
title: 'Changes saved',
description: 'Your profile is up to date.',
});
toast.dismiss(id);
// One id, two moments: the second toast takes the first one's place.
toast({ id: 'upload', title: 'Uploading…', duration: Infinity });
toast({ id: 'upload', tone: 'success', title: 'Uploaded' });
toast({
title: 'Message archived',
action: { label: 'Undo', onClick: () => restore() },
});

toast() works the same from a React component, an Astro page script or any other script: it dispatches a zd:toast event on the document, and a call made before the toaster has started waits for it. It needs no provider and no hook.

These are static previews; the toasts above are the real thing.

Draft saved

You can come back to it any time.

Changes saved

Your profile is up to date.

Storage almost full

92% of your plan is in use.

Payment failed

The card was declined.

With an action, and without a dismiss button

Section titled “With an action, and without a dismiss button”

Message archived

It moved to the archive folder.

Uploading 3 files…

<Toast
title="Message archived"
action={
<Button asChild label="Undo" variant="link" size="sm">
<Link href="/inbox?undo=1" />
</Button>
}
/>

Toast, for a toast the server renders inside a Toaster:

Prop Type Default Notes
tone 'info' | 'success' | 'warning' | 'danger' 'info' The icon and its tile. danger interrupts, and stays until it is dismissed.
title string — What happened, in a few words.
description string — What it means or what happens next.
action ReactNode — One thing to do about it, under the description: a link Button, size="sm".
dismissible boolean true The dismiss button, and Escape.
duration number toaster’s Milliseconds it stays once enhanced; Infinity keeps it.
labels Partial<ToastLabels> English { dismiss }. es.toast from @zitdevs/ui/labels/es.
id string — Lets toast.dismiss(id) reach a toast the server rendered.
ref Ref<HTMLDivElement> — The toast.
anything else <div> attributes — Except role: the toaster’s regions announce it.

toast(options) from @zitdevs/ui/enhance, which returns the toast’s id:

Option Type Default Notes
title string — A toast with neither a title nor a description is not shown.
description string —
tone ToastTone 'info'
duration number 5000 Milliseconds; Infinity keeps it. Ignored by danger, which stays until dismissed.
dismissible boolean true
action { label: string; onClick(event): void } — The toast leaves after the click unless onClick calls event.preventDefault().
id string made up Dismisses it later, or replaces the toast already shown under it.
onDismiss (id: string) => void — Called once it has left, however it went.

toast.dismiss(id) dismisses one toast; toast.dismiss() every one. Exported types: ToastProps, ToastTone, ToastLabels; from @zitdevs/ui/enhance: toast, TOAST_EVENT, ToastOptions, ToastAction, ToastRequest, ToastTone.

  • Announced once, in its turn. Info, success and warning go to the toaster’s polite live region (role="status"); danger to its assertive one (role="alert"). Both regions are on the page, empty, from the first paint, so assistive technology has met them before anything arrives.
  • Timed so it can be read. A toast leaves after five seconds by default, and every timer stops while the pointer is over the toasts, while focus is inside them, and while the page is hidden. Danger toasts stay until they are dismissed (WCAG 2.2.1). A toast whose action matters, such as “Undo”, is better kept with duration: Infinity.
  • Dismissed without losing your place. Its dismiss button, or Escape on a focused toast, removes it, and focus moves to the next toast or back to where it was before it entered them — never to the top of the page.
  • Never the only place it is said. A toast reports what the page already shows; a failure the reader must act on also belongs next to its cause, as a field’s error or an Alert.
  • At most three show at once. The oldest leaves first, and an error last.
  • The title is set in ink and the description in muted text, both at 4.5:1 or more on the raised card. The tone is in the icon and the words, never the colour alone.
  • It fades in over 200ms with a 4px rise, and out over 140ms; neither moves under reduced motion.

A toast the server renders inside a Toaster — the flash message of the page a form’s POST returns — shows with no script at all, and stays: there is nothing to time it. Its dismiss button waits out of sight until the toaster is enhanced, since it would do nothing before. Once the page’s script runs enhanceToaster(document) (React does it on hydration), the flash message is announced, timed and dismissible like any other. toast() needs the script.

surface-raised, border-subtle, ink, muted, and for the tiles info, info-soft, success, success-soft, warning, warning-soft, danger, danger-soft; shadow-lg. The glyphs reach 3:1 on their tiles except warning’s, at 2.99:1, which only decorates: the title says it.

Use instead: a message that must stay with the form it is about → Alert; a decision the reader must make before going on → Dialog.