Skip to main content

Design system components

Toast

Transient status message with tone, position, and auto-dismiss.

Status: stable. Group: feedback. Markdown version: /design-system/components/toast.md

When to use

Toast is the transient "confirmation after action" surface: it appears, announces itself, and auto-dismisses. Most consumers never mount <Toast /> directly; the app-root ToastProvider (@/providers/ToastProvider) owns the single live region and exposes the imperative API: useToast() returns { showToast }, where showToast(message: string, options?: number | { tone?: ToastTone; duration?: number; position?: ToastPosition }) accepts a bare duration in ms (legacy) or an options object (duration defaults to 3000). The element stays mounted as a live region and swaps the message in/out, so assistive tech reliably announces every toast; errors and warnings escalate to role=alert automatically.

When not to use

  • Do not mount two overlapping fixed <Toast /> instances yourself; use ToastStack (or the app ToastProvider) so concurrent messages stack instead of replacing each other.
  • Do not rely on Toast for compliance acknowledgements (cookie consent, legal notices); it auto-dismisses; use AlertBanner or Modal.
  • Do not use Toast for validation errors on a field; the message detaches from the input; use the field's error prop.
  • Do not use Toast for persistent status, compliance acknowledgements, or anything the user must read; it disappears on its own. AlertBanner owns those.

Look-alikes

  • AlertBanner: Do not use Toast for persistent status, compliance acknowledgements, or anything the user must read; it disappears on its own. AlertBanner owns those.
  • Modal: Do not rely on Toast for compliance acknowledgements (cookie consent, legal notices); it auto-dismisses; use AlertBanner or Modal.
  • ToastStack: Do not mount two overlapping fixed <Toast /> instances yourself; use ToastStack (or the app ToastProvider) so concurrent messages stack instead of replacing each other.

Props

Toast props
PropTypeDefaultDescriptionRequired
openbooleannoneControls visibility.No
tone"info" | "success" | "warning" | "error" | "neutral"neutralSemantic colour.No
position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"bottom-centerPosition on screen.No
size"sm" | "md" | "lg"mdSize.No
messagestringnoneMessage text displayed in the toast.Yes
durationnumber3000Auto-dismiss delay in ms.No
onClose() => voidnoneCalled when the auto-dismiss timer fires.No
onDismiss() => voidnoneRenders a dismiss button that calls this, so a toast can be closed by hand and keyboard users can reach it (focus inside a ToastStack pauses its timers). The button sits outside the live region.No
dismissLabelstringnoneAccessible name of the dismiss button. Defaults to the translated "Dismiss notification" (toast.dismiss).No
inlinebooleannoneRender in normal document flow instead of fixed-positioning itself; a parent (ToastStack) owns placement. position is ignored when set.No

Variants

  • Available sizes: sm, md, lg (default md). Forwarded to CSS module size classes.
  • Available tones: neutral, success, error, warning, info (default neutral).
  • Available positions: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right (default bottom-center).

Invalid combinations

  • SHOULD NOT: When inline is true, then do not set position. An inline toast is placed by its parent (ToastStack); position is ignored.

Keyboard

The contract records no keyboard interaction.

Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

Toast rules
LevelRuleHow it is checked
SHOULD NOTWhen inline is true, then do not set position. An inline toast is placed by its parent (ToastStack); position is ignored.Checked by the MCP tool validate_component_usage (rule toast-inline-ignores-position). Also enforced in code: Toast.tsx: inline selects toast--inline instead of the position class.
MUSTARIA: Role=status + aria-live=polite for neutral/info/success.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Role=alert + aria-live=assertive for warning/error.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Aria-atomic=true so the full message is announced.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTARIA: Stays mounted as a live region; message swaps in/out.Not yet verified: no evidence record. WCAG 4.1.2.
MUSTRequired stories produce no axe violations at the configured severity.Automated by npm run test:stories:matrix:ci. WCAG 2.5.8.
MUSTThe accessibility tree (role, name, state) matches the committed snapshot for every required story.Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2.
MUSTThe component remains legible and operable under a real browser forced-colors: active pass.Automated by npm run test:stories:hc.
MUSTA human completed an end-to-end accessibility review of this component.Manual review, last on 2026-11-04.
SHOULDFire toasts via useToast().showToast; the provider is mounted once at the app shell and handles the live region, position, and dismissal.Not stated in the contract.
SHOULDKeep the message to one short sentence; the toast auto-dismisses and cannot be paused, so long copy will be cut off mid-read.Not stated in the contract.
SHOULDPass tone so the filled icon carries the meaning alongside color (WCAG 1.4.1); error and warning announce assertively on their own.Not stated in the contract.
SHOULDRaise duration (e.g. 5000-6000ms) for messages the user may need to act on, like a network-retry notice.Not stated in the contract.
SHOULD NOTDo not mount two <Toast /> instances side by side; the auto-dismiss timer is per instance but the announcement doubles in the AT queue.Not stated in the contract.
SHOULD NOTDo not put field validation errors in a toast; the message detaches from the field it describes; use the input's error prop.Not stated in the contract.

Install and import

  • Install: npm install @digitaltableteur/react (version 0.9.1, restricted access on npm).
  • Import: import { Toast } from "@digitaltableteur/react";
  • Entry point: @digitaltableteur/react/feedback exports it too.
  • Styles: import @digitaltableteur/react/style.css once at the app boundary.
  • Also available as the custom element <dt-toast>; see the custom elements manifest.