Dev view

Toast

A transient floating notification anchored to a corner of the viewport, auto-dismissing after a short visible duration. Distinct from Alert (inline, persistent) by its ephemerality and positioning; distinct from Modal (blocking) by its non-modal contract. Used for success confirmations after asynchronous actions, transient status messages, undoable operations, and short notifications that do not require explicit acknowledgement.

When to use

Use

For transient confirmations after asynchronous actions ("Saved", "Copied"), undoable operations ("Item deleted · Undo"), and short non-essential status updates the user can safely miss. Toast is non-blocking and auto-dismisses; users do not need to acknowledge.

Avoid

For critical errors requiring user action — that is `Alert` (inline persistent) or `Modal` (blocking). For long informational content — that is `Alert` or page prose. For on-demand description — that is `Tooltip`. For page-wide persistent notices — that is `Banner`.

Versus related

  • alert

    `Alert` is inline and persistent; `Toast` is floating and auto-dismissed. Alert is for situations the user may need to revisit or act on; Toast is for transient signals the user can miss without consequence.

  • modal

    `Modal[variant=alertdialog]` is blocking and demands explicit response; `Toast` is non-blocking and ephemeral. They occupy opposite ends of the notification-urgency spectrum.

  • banner

    `Banner` is page-wide, persistent, and informational (e.g. a maintenance notice at the top of every page); `Toast` is corner-anchored, ephemeral, and action-triggered. Banner is system-level; Toast is action-level.

  • tooltip

    `Tooltip` is hover/focus-revealed descriptive text; `Toast` is system-pushed notification. Tooltip is pulled by the user; Toast is pushed by the system.

Highlight
Fig 1.1 · Toast · Dev view
Dev

Code anatomy

Slot Code slot Semantic
viewport viewport presentational-region
container container status-or-alert
icon icon presentational
title title strong-or-heading
body body prose
action action button
dismiss-button dismiss button
Both

Variants, properties, states

Variants

Structurally different versions of the component.

infosuccesswarningerror

Properties

The same component, parameterised.

PropertyType
positiontop-start | top-end | bottom-start | bottom-end | top-center | bottom-center
dismissibleboolean
hasActionboolean
hasIconboolean

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visible
data
openingopenpausedclosingclosed
Both

State transitions

FromToTrigger
closedopeningConsumer triggers the toast (typically after an async operation completes). The toast is mounted in the viewport region; the auto-dismiss timer is initialised but not yet started.
openingopenThe slide-in animation completes (or, under prefers-reduced-motion reduce, immediately). The auto-dismiss timer starts; the live region announces the toast content.
openpausedUser hovers anywhere inside the toast viewport region OR focus moves into the toast region. The auto-dismiss timer pauses for all toasts in the viewport, not just the hovered one.
pausedopenUser leaves the viewport region (pointer and focus both out). The auto-dismiss timer resumes from where it paused (NOT restarted from full duration — the elapsed time before pause counts).
openclosingThe auto-dismiss timer expires; or the user presses Escape with focus inside the toast region; or the user activates the dismiss button or invokes the action; or the consumer programmatically closes the toast.
closingclosedThe slide-out animation completes (or immediately under reduced motion). The toast is removed from the viewport and from the live region. Subsequent toasts in the stack reflow.
Dev

Cross-framework expression

FrameworkStructure mechanismVariant mechanism
Web Components A `<ui-toast-viewport>` host wrapping `<ui-toast>` children; viewport handles stacking and pause-on-hover; toast carries severity attribute reflected to role attributes (`severity="success"`, `position="bottom-end"`, `dismissible`, `auto-dismiss-ms="5000"`); `data-state="opening|open|paused|closing|closed"`
React Radix Toast (`Toast.Provider` / `Toast.Viewport` / `Toast.Root` / `Toast.Title` / `Toast.Description` / `Toast.Action`); React Aria `useToastRegion` plus `useToast`; or Sonner library which composes its own viewport props with class-variance-authority for severity; `duration` prop for auto-dismiss; `position` on the viewport
Angular (signals) Angular CDK Overlay for the viewport positioning plus `MatSnackBar` (or custom service) for queued toasts; signal-based toast state input<'info' | 'success' | 'warning' | 'error'>(); service-level `position` and `duration` defaults
Vue Headless UI does not ship Toast as of 2026-04; third-party (vue-sonner, primevue Toast); custom composable `useToast` plus a singleton viewport component defineProps with literal-union types; service-level configuration
Both

Events

  1. openChange
    Payload
    Boolean. `true` after the toast has finished entering, `false` after it has finished exiting (whether by auto-dismiss, user action, or programmatic close).
    Web Components
    `openChange` CustomEvent on the toast element with `event.detail = { open: boolean }`.
    React
    `onOpenChange(open: boolean)` controlled-pattern callback on `Toast.Root` (Radix Toast).
    Angular Signals
    `output<boolean>('openChange')` on the toast component or a service-level observable for queued toasts.
    Vue
    `@update:open` for `v-model:open` on the toast element.
  2. dismiss
    Payload
    `{ source: 'autoDismiss' | 'closeButton' | 'escape' | 'swipe' | 'action' | 'programmatic' }`. Distinguishes how the toast was dismissed. Useful for analytics ("how often is auto-dismiss vs explicit dismiss") and for consumers that want to suppress the action handler on auto-dismiss but run it on explicit dismiss.
    Web Components
    `dismiss` CustomEvent with `event.detail = { source }`.
    React
    `onDismiss(source)` callback. Some libraries surface per-source callbacks (`onSwipeEnd`, `onEscapeKeyDown`) instead; consumers wrap to recover the canonical contract.
    Angular Signals
    `output<DismissSource>('dismiss')`.
    Vue
    `@dismiss` event with payload `{ source }`.
  3. actionInvoke
    Payload
    `{ actionId: string }` — fires when the user activates the action slot's button. Consumers handle the effect (typically undo or navigation); the toast dismisses automatically as a side effect with `dismiss { source: 'action' }`.
    Web Components
    `actionInvoke` CustomEvent with `event.detail = { actionId }`.
    React
    `onAction(actionId)` callback or `onClick` on the `Toast.Action` element.
    Angular Signals
    `output<string>('actionInvoke')`.
    Vue
    `@action-invoke` event with payload `{ actionId }`.
Dev

Performance thresholds

  • stackDepthvisible-toast-count3toasts

    The canonical maximum of visible toasts in the viewport is 3. Above this threshold, SR announcement floods occur and the viewport overflows on common screen sizes. Beyond 3, queue new toasts and reveal them as existing toasts dismiss. Mobile drops the threshold to 2 (see responsive).

  • autoDismissDurationvisible-time5000ms

    The canonical default auto-dismiss duration is 5 seconds. Below 3 seconds users cannot reliably read the message; above 8 seconds the screen real estate cost outweighs the benefit. The 5000ms default is reachable from `Sonner` / Radix Toast / React Aria defaults. Long-text locales may extend per the i18n textExpansion formula.

Both

Accessibility

Slot Accessibility hint
viewport Apply `role="region"` with `aria-label="Notifications"` so SR users can navigate to the toast region by landmark. The region itself is not a live region — individual toasts inside carry their own polite or assertive live-region roles depending on severity. Hover anywhere inside the viewport pauses the auto-dismiss timers of all visible toasts.
container Apply `role="status"` (polite, default) for info and success severities, or `role="alert"` (assertive) for error severities. Warning sits on the boundary — canonical default is polite to avoid interrupting the user's work. Both roles imply implicit live regions.
icon Decorative — `aria-hidden="true"`. Severity is communicated through the `role` and the visible body text, never through the icon glyph alone.
title Plain `<strong>` or styled `<p>`; heading semantics not required for transient toasts. SR announces the title followed by the body in DOM order.
body Plain prose. Capped at one to two sentences by canon. Anything longer should be an Alert.
action Real button with accessible name. Receives focus when the user tabs into the toast region (after the dismiss button); pressing Tab exits the region. Activation triggers the consumer-provided handler and typically dismisses the toast as a side effect.
dismiss-button Provide an accessible name ("Dismiss" or "Dismiss notification"). Activation removes the toast from the viewport and from the live region. Escape key (when toast region has focus) does the same.
Both

Accessibility acceptance

Keyboard walk

KeysExpected
F6 (when supported by browser/OS)Cycles focus to landmark regions including the toast viewport. Allows keyboard users to reach the toast region without tabbing through the entire page first.
Tab (within the toast viewport)Focus moves through visible toasts in DOM order — newest toast first by canonical convention (the one most recently announced). Within each toast, focus moves through action first then dismiss button.
Escape (focus inside toast viewport)Dismisses the focused toast (or most recent if none focused). Focus moves to the next focusable in the viewport, or back out to the document if no other toast remains.
Enter or Space (focus on action button)Invokes the action; toast dismisses with `source: 'action'`. Consumer handler runs.
Enter or Space (focus on dismiss button)Dismisses the toast immediately with `source: 'closeButton'`.

Screen-reader announcements

TriggerExpected
Polite-severity toast appears (info, success, warning)SR queues the announcement after the current one completes. Reads the role ("status") followed by title and body in DOM order. Does not interrupt the user's current task.
Assertive-severity toast appears (error)SR interrupts the current announcement and reads the toast immediately. Reads "alert" followed by title and body. Used sparingly — assertive is for genuinely urgent situations.
User hovers the viewport, timer pausesNo announcement — the pause is internal state. SR users can re-read by navigating to the toast region's content.
Toast auto-dismisses or is closedNo "dismissed" announcement; the silence (and removal of the live-region content) confirms the toast is gone.

axe-core rules to assert

  • aria-allowed-role
  • aria-required-attr
  • color-contrast
  • role-img-alt
  • landmark-unique
Dev

Common mistakes

#toast-no-pause-on-hover

Auto-dismiss continues while the user reads

Problem

The toast slides in, the user starts reading, the timer fires, the toast slides out. Users with motor impairments, cognitive impairments, or simply slow reading speed cannot reliably consume the message. WCAG 2.2.1 (Timing Adjustable) is failed.

Fix

Pause the auto-dismiss timer on pointer-enter and focus-into the viewport region. Resume on pointer-leave AND focus-out (both must be true). Some implementations pause on individual toast hover; canonical pauses on viewport-level hover so users can move between adjacent toasts.

#toast-stack-overflow

Toast stack grows unbounded

Problem

Multiple toasts fire in rapid succession; the stack grows to 5, 10, 20 toasts. SR users hear a torrent of announcements; pointer users see the screen filled with notifications. The viewport overflows.

Fix

Cap the canonical stack depth at 3 visible toasts. Beyond that, queue new toasts and only render them when an existing toast dismisses. Document the threshold in performance. The queue is invisible — users do not know how many toasts are pending.

#toast-essential-info

Critical errors delivered as toast

Problem

A network failure or data-loss warning is shown as an auto-dismissing toast. The user misses it (auto-dismiss); the underlying problem persists; data is lost without recourse.

Fix

Critical errors that require user action are Alert (inline persistent) or Modal (blocking). Toast is reserved for transient confirmations and non-essential updates the user can miss without consequence. Document the canonical severity-to-component map: success and info → toast or alert; warning → alert; error requiring action → alert or modal.

#toast-aria-live-assertive-for-info

Info-severity toast uses assertive live region

Problem

Toasts carry `role="alert"` (assertive) regardless of severity. SR users are interrupted for every "Saved" / "Copied to clipboard" confirmation; the assertive flood makes them disable notifications entirely.

Fix

Match severity to live-region politeness: info / success → `role="status"` (polite), error → `role="alert"` (assertive), warning is canonical-default polite (re-evaluate per use case). Document the mapping; reserve assertive for content that genuinely interrupts.

#toast-no-keyboard-dismiss

Escape does not dismiss the focused toast

Problem

Keyboard users tabbing into the toast region cannot exit via Escape — the only way to dismiss is to tab to the dismiss button and activate, or wait for auto-dismiss. The keyboard contract feels broken compared to other light-dismiss patterns (Popover, Tooltip).

Fix

Bind Escape on the viewport region: when focus is inside the toast region, Escape dismisses the focused toast (or the most recent toast if none is focused). Document the keyboard contract in a11yAcceptance.

Figma↔Code mismatches
  1. 01
    Figma

    A toast drawn next to the trigger that surfaced it

    Code

    A toast rendered in a fixed-position viewport region at a corner of the screen

    Consequence

    Designers may anchor toasts to specific UI elements in mocks (next to the save button, below the form). Implementations following the Figma file create per-trigger floating surfaces that compete with each other and miss the stacking, the auto-dismiss, and the live-region semantics.

    Correct

    Toast lives in a single viewport region per page (or per window scope), not anchored to triggers. The position property selects which corner; the viewport region handles stacking and live-region semantics. Document the singleton viewport contract explicitly.

  2. 02
    Figma

    Toast auto-dismiss drawn as a single visual state with no progress indicator

    Code

    An auto-dismiss timer driving slide-out at a configurable duration

    Consequence

    Designers may not realise the toast disappears automatically; developers ship without a progress affordance and users feel surprised when the toast vanishes mid-read. Worse, implementations may default to too-short or too-long durations.

    Correct

    Document auto-dismiss as a canonical behaviour with a configurable duration property (or a fixed canonical default such as 5 seconds). An optional progress indicator slot may visualise remaining time. Hover-pause is canonical to protect users who are reading.

  3. 03
    Figma

    Multiple toast variants for stacking visualisation

    Code

    A single toast component plus a viewport region that handles stacking

    Consequence

    Designers create "single toast", "two toasts", "three toasts" variants in the design file. Developers ship one toast plus stacking logic; the design file's three-toast variant is decorative, not real, and the production stack behaves differently.

    Correct

    Stacking is a viewport-region concern, not a toast variant. Document the canonical stack-depth maximum and the stacking animation in the viewport's anatomy slot. The toast itself is a single component; the viewport orchestrates.

  4. 04
    Figma

    Toast action drawn as link-styled inline text inside body

    Code

    Toast action as a Button in the action slot

    Consequence

    Designers compose body text with inline link "Undo"; the embedded interaction is either a real anchor (wrong semantic — Undo does not navigate) or a span with click handler (no semantic at all). Keyboard reachability and announcement contracts both suffer.

    Correct

    Single action lives in the dedicated action slot as a Button element (typically with `variant: tertiary` or `ghost`). The body describes the situation; the action is separately reachable by Tab.