Toast

Display temporary notifications and messages to users with automatic dismissal and customizable placement

Usage

import { ToastProvider, toast } from '@hareui/vue'
<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, toast } from '@hareui/vue'import { h } from 'vue'function show() {  const id = toast('You have been invited to join a team', {    description: 'Bob sent you an invitation to join HeroUI team',    indicator: h(Icon, { icon: 'gravity-ui:persons' }),    variant: 'default',    action: {      label: 'Dismiss',      variant: 'tertiary',      onClick: () => toast.close(id),    },  })}</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <Button size="sm" variant="secondary" @click="show">      Show toast    </Button>  </div></template>

Anatomy

A single ToastProvider renders the whole stack; each toast's layout (indicator, title, description, action, close button) is built in, or fully replaced through the #toast slot (see Custom Toast Rendering).

<ToastProvider />
toast('Event created')

ui keys: region, toast, content, indicator, title, description, close, action.

Examples

Variants

<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, toast } from '@hareui/vue'import { h } from 'vue'function showDefault() {  const id = toast('You have been invited to join a team', {    description: 'Bob sent you an invitation to join HeroUI team',    indicator: h(Icon, { icon: 'gravity-ui:persons' }),    variant: 'default',    action: { label: 'Dismiss', variant: 'tertiary', onClick: () => toast.close(id) },  })}function showAccent() {  const id = toast.info('You have 2 credits left', {    description: 'Get a paid plan for more credits',    action: { label: 'Upgrade', onClick: () => toast.close(id) },  })}function showSuccess() {  const id = toast.success('You have upgraded your plan', {    description: 'You can continue using HareUI Chat',    action: { label: 'Billing', class: 'bg-success text-success-foreground', onClick: () => toast.close(id) },  })}function showWarning() {  const id = toast.warning('You have no credits left', {    description: 'Upgrade to a paid plan to continue',    action: { label: 'Upgrade', class: 'bg-warning text-warning-foreground', onClick: () => toast.close(id) },  })}function showDanger() {  const id = toast.danger('Storage is full', {    description: 'Remove files to release space. Adding more text to demonstrate longer content display',    indicator: h(Icon, { icon: 'gravity-ui:hard-drive' }),    action: { label: 'Remove', variant: 'danger', onClick: () => toast.close(id) },  })}</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <div class="flex w-full flex-wrap items-center justify-center gap-4">      <Button size="sm" variant="tertiary" @click="showDefault">        Default toast      </Button>      <Button size="sm" variant="secondary" @click="showAccent">        Accent toast      </Button>      <Button class="text-success-soft-foreground" size="sm" variant="tertiary" @click="showSuccess">        Success toast      </Button>      <Button class="text-warning-soft-foreground" size="sm" variant="tertiary" @click="showWarning">        Warning toast      </Button>      <Button size="sm" variant="danger-soft" @click="showDanger">        Danger toast      </Button>    </div>  </div></template>

Placements

<script setup lang="ts">import { Button, ToastProvider, ToastQueue } from '@hareui/vue'const placements = ['top start', 'top', 'top end', 'bottom start', 'bottom', 'bottom end'] as consttype Placement = typeof placements[number]// A separate queue per placement, so each `ToastProvider` below stacks independently.const placementQueues: Record<Placement, ToastQueue> = Object.fromEntries(  placements.map(p => [p, new ToastQueue({ maxVisibleToasts: 3 })]),) as Record<Placement, ToastQueue>function showToast(placement: Placement) {  placementQueues[placement].add({ title: 'Event created', description: 'Event has been created', variant: 'default' })}</script><template>  <div class="flex h-full flex-col items-center justify-center gap-6">    <ToastProvider v-for="p in placements" :key="p" :placement="p" :queue="placementQueues[p]" />    <div class="flex max-w-xs flex-wrap justify-center gap-2">      <Button v-for="p in placements" :key="p" size="sm" variant="secondary" @click="showToast(p)">        {{ p }}      </Button>    </div>  </div></template>

Expanded Stack

<script setup lang="ts">import { Button, ToastProvider, ToastQueue } from '@hareui/vue'const queue = new ToastQueue()function show() {  queue.add({ title: 'Simple message', variant: 'default' })  setTimeout(() => queue.add({ title: 'Operation completed', variant: 'success' }), 400)  setTimeout(() => queue.add({ title: 'New update available', variant: 'accent' }), 800)}</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <ToastProvider is-expanded aria-label="Expanded notifications" placement="bottom" :queue="queue" />    <Button size="sm" variant="secondary" @click="show">      Show 3 toasts    </Button>  </div></template>

Simple Toasts

<script setup lang="ts">import { Button, toast } from '@hareui/vue'</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <div class="flex w-full flex-wrap items-center justify-center gap-4">      <Button size="sm" variant="secondary" @click="toast('Simple message')">        Default      </Button>      <Button size="sm" variant="secondary" @click="toast.success('Operation completed')">        Success      </Button>      <Button size="sm" variant="secondary" @click="toast.info('New update available')">        Info      </Button>      <Button size="sm" variant="secondary" @click="toast.warning('Please check your settings')">        Warning      </Button>      <Button size="sm" variant="secondary" @click="toast.danger('Something went wrong')">        Error      </Button>    </div>  </div></template>

Custom Indicators

<script setup lang="ts">import { Icon } from '@iconify/vue'import { Button, toast } from '@hareui/vue'import { h } from 'vue'function show() {  toast('Custom icon indicator', { indicator: h(Icon, { icon: 'gravity-ui:star' }) })}</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <Button size="sm" variant="secondary" @click="show">      Custom indicator    </Button>  </div></template>

Custom Toast Rendering

<script setup lang="ts">import type { ToastContentValue } from '@hareui/vue'import { Button, CloseButton, ToastProvider, ToastQueue } from '@hareui/vue'const customQueue = new ToastQueue()function show() {  customQueue.add({ title: 'Custom layout toast', description: 'This uses a custom render function', variant: 'default' })}</script><template>  <div class="flex h-full max-w-xl flex-col items-center justify-center">    <ToastProvider placement="bottom" :queue="customQueue" :ui="{ toast: 'rounded-xl border border-border' }">      <template #toast="{ content, close }">        <div class="flex items-center gap-2">          <svg aria-hidden="true" class="size-4 shrink-0 text-accent-soft-foreground" fill="none" viewBox="0 0 16 16">            <path clip-rule="evenodd" d="M8 13.5a5.5 5.5 0 1 0 0-11a5.5 5.5 0 0 0 0 11M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14m1-9.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0m-.25 3a.75.75 0 0 0-1.5 0V11a.75.75 0 0 0 1.5 0z" fill="currentColor" fill-rule="evenodd" />          </svg>          <div class="flex flex-col pe-6">            <p v-if="(content as ToastContentValue).title" class="text-sm font-medium text-accent-soft-foreground">              {{ (content as ToastContentValue).title }}            </p>            <p v-if="(content as ToastContentValue).description" class="text-sm text-muted">              {{ (content as ToastContentValue).description }}            </p>          </div>        </div>        <CloseButton class="absolute end-2 top-1/2 -translate-y-1/2 border-none bg-transparent opacity-100 [&>svg]:size-4" @click="close" />      </template>    </ToastProvider>    <Button size="sm" variant="secondary" @click="show">      Custom toast    </Button>  </div></template>

Promise & Loading

Using toast.promise()

Automatically handles loading, success, and error states

Manual Loading State

Manually control loading state with isLoading prop

<script setup lang="ts">import { Button, toast } from '@hareui/vue'function uploadFile(): Promise<{ filename: string, size: number }> {  const { promise, resolve } = Promise.withResolvers<{ filename: string, size: number }>()  setTimeout(() => resolve({ filename: 'document.pdf', size: 1024 }), 2000)  return promise}function createEvent(): Promise<never> {  const { promise, reject } = Promise.withResolvers<never>()  setTimeout(() => reject(new Error('Network error. Please try again.')), 2000)  return promise}function saveData(): Promise<{ count: number }> {  const { promise, resolve, reject } = Promise.withResolvers<{ count: number }>()  setTimeout(() => {    if (Math.random() > 0.5)      resolve({ count: 42 })    else      reject(new Error('Failed to save data'))  }, 2000)  return promise}function fetchUser(): Promise<{ name: string, email: string }> {  const { promise, resolve } = Promise.withResolvers<{ name: string, email: string }>()  setTimeout(() => resolve({ email: '[email protected]', name: 'John Doe' }), 2000)  return promise}function uploadWithLoading() {  const loadingId = toast('Uploading file...', { description: 'Please wait while we upload your file', isLoading: true, timeout: 0 })  setTimeout(() => {    toast.update(loadingId, 'File uploaded', { description: 'Your file has been uploaded successfully', variant: 'success' })  }, 3000)}function paymentProcessing() {  const loadingId = toast('Processing payment...', { isLoading: true, timeout: 0 })  setTimeout(() => {    toast.update(loadingId, 'Payment processed', { description: 'Your payment has been processed successfully', variant: 'success' })  }, 2500)}function loadingToError() {  const loadingId = toast('Saving changes...', { isLoading: true, timeout: 0 })  setTimeout(() => {    toast.update(loadingId, 'Failed to save', { description: 'Please try again', variant: 'danger' })  }, 2000)}</script><template>  <div class="flex h-full max-w-2xl flex-col items-center justify-center gap-8">    <div class="w-full space-y-3">      <div class="text-center">        <h3 class="text-sm font-medium">          Using toast.promise()        </h3>        <p class="text-xs text-muted">          Automatically handles loading, success, and error states        </p>      </div>      <div class="flex w-full flex-wrap items-center justify-center gap-4">        <Button          size="sm"          variant="secondary"          @click="toast.promise(uploadFile(), {            loading: 'Uploading file...',            success: data => `File ${data.filename} uploaded (${data.size}KB)`,            error: 'Failed to upload file',          })"        >          Upload file        </Button>        <Button          size="sm"          variant="secondary"          @click="toast.promise(createEvent(), {            loading: 'Creating event...',            success: 'Event created',            error: err => err.message,          })"        >          Create event (error)        </Button>        <Button          size="sm"          variant="secondary"          @click="toast.promise(saveData(), {            loading: 'Saving changes...',            success: data => `Saved ${data.count} items`,            error: err => err.message,          })"        >          Save data (random)        </Button>        <Button          size="sm"          variant="secondary"          @click="toast.promise(fetchUser(), {            loading: 'Loading user...',            success: data => `Welcome back, ${data.name}!`,            error: 'Failed to fetch user',          })"        >          Fetch user        </Button>      </div>    </div>    <div class="w-full space-y-3">      <div class="text-center">        <h3 class="text-sm font-medium">          Manual Loading State        </h3>        <p class="text-xs text-muted">          Manually control loading state with isLoading prop        </p>      </div>      <div class="flex w-full flex-wrap items-center justify-center gap-4">        <Button size="sm" variant="secondary" @click="uploadWithLoading">          Upload with loading        </Button>        <Button size="sm" variant="secondary" @click="paymentProcessing">          Payment processing        </Button>        <Button size="sm" variant="secondary" @click="loadingToError">          Loading to error        </Button>      </div>    </div>  </div></template>

Callbacks

Closed History

No toasts closed yet. Try closing one above!

<script setup lang="ts">import { Button, toast } from '@hareui/vue'import { ref } from 'vue'const closedHistory = ref<Array<{ message: string, time: string }>>([])function addToHistory(message: string) {  const time = new Date().toLocaleTimeString()  closedHistory.value = [{ message, time }, ...closedHistory.value].slice(0, 5)}</script><template>  <div class="flex h-full max-w-2xl flex-col items-center justify-center gap-6">    <div class="flex w-full flex-wrap items-center justify-center gap-4">      <Button        size="sm"        variant="secondary"        @click="toast('File saved', { timeout: 3000, onClose: () => addToHistory('File saved (closed after 3 seconds)') })"      >        Custom timeout (3s)      </Button>      <Button        size="sm"        variant="secondary"        @click="toast('Changes saved', { timeout: 10000, onClose: () => addToHistory('Changes saved (closed after 10 seconds)') })"      >        Custom timeout (10s)      </Button>      <Button        size="sm"        variant="secondary"        @click="toast.success('Event created', { onClose: () => addToHistory('Event created (closed after default timeout)') })"      >        With onClose callback      </Button>      <Button        size="sm"        variant="secondary"        @click="toast('Important notification', {          description: 'This toast will stay until dismissed',          timeout: 0,          onClose: () => addToHistory('Important notification (manually closed)'),        })"      >        Persistent toast      </Button>    </div>    <div class="w-full space-y-2">      <div class="flex items-center justify-between">        <h3 class="text-sm font-medium">          Closed History        </h3>        <Button v-if="closedHistory.length > 0" class="h-6 text-xs" size="sm" variant="tertiary" @click="closedHistory = []">          Clear        </Button>      </div>      <div class="min-h-[120px] space-y-2 rounded-lg border border-border bg-surface p-4">        <p v-if="closedHistory.length === 0" class="text-sm text-muted">          No toasts closed yet. Try closing one above!        </p>        <div          v-for="(item, index) in closedHistory"          :key="`${item.time}-${index}`"          class="flex animate-in items-start justify-between gap-3 rounded-md border border-border bg-default px-3 py-2 text-sm duration-200 fade-in slide-in-from-top-2"          :style="{ animationDelay: `${index * 50}ms` }"        >          <div class="flex-1">            <span class="font-medium">{{ item.message }}</span>            <span class="ms-2 text-xs text-muted">({{ item.time }})</span>          </div>          <div class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-success/10 text-success-soft-foreground">            <svg class="size-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">              <path d="M5 13l4 4L19 7" stroke-linecap="round" stroke-linejoin="round" />            </svg>          </div>        </div>      </div>    </div>  </div></template>

Custom Queues

<script setup lang="ts">import { Button, ToastProvider, ToastQueue } from '@hareui/vue'const notificationQueue = new ToastQueue({ maxVisibleToasts: 2 })const errorQueue = new ToastQueue({ maxVisibleToasts: 3 })const successQueue = new ToastQueue({ maxVisibleToasts: 1 })</script><template>  <div class="flex h-full max-w-4xl items-center justify-center gap-4">    <ToastProvider placement="bottom" :queue="notificationQueue" />    <div class="flex justify-center gap-2">      <Button        size="sm"        variant="secondary"        @click="notificationQueue.add({ title: 'New notification', description: 'You have a new message', variant: 'default' })"      >        Add notification (max 2)      </Button>    </div>    <ToastProvider placement="bottom start" :queue="errorQueue" />    <div class="flex justify-center gap-2">      <Button        size="sm"        variant="danger-soft"        @click="errorQueue.add({ title: 'Error occurred', description: 'Failed to save changes', variant: 'danger' })"      >        Add error (max 3)      </Button>    </div>    <ToastProvider placement="bottom end" :queue="successQueue" />    <div class="flex justify-center gap-2">      <Button        class="text-success-soft-foreground"        size="sm"        variant="secondary"        @click="successQueue.add({ title: 'Success!', description: `Operation ${Date.now()}`, variant: 'success' })"      >        Add success (max 1)      </Button>    </div>  </div></template>

Setup

Render the provider once at the root of your app.

<script setup lang="ts">
import { Button, ToastProvider, toast } from '@hareui/vue'
</script>

<template>
  <ToastProvider />
  <Button @click="toast('Simple message')">
    Show toast
  </Button>
</template>

Customization

Tailwind CSS

<script setup lang="ts">import type { ToastContentValue } from '@hareui/vue'import { Button, ToastProvider, ToastQueue } from '@hareui/vue'const queue = new ToastQueue()function show() {  queue.add({ title: 'Saved', description: 'Draft synced', variant: 'default' })}</script><template>  <div class="flex flex-col items-center gap-4">    <ToastProvider      placement="bottom"      :queue="queue"      :ui="{ toast: 'rounded-xl border border-border/80 bg-surface shadow-lg ring-1 ring-black/5 dark:ring-white/10' }"    >      <template #toast="{ content }">        <div class="flex items-center gap-2.5">          <svg aria-hidden="true" class="size-4 shrink-0 text-neutral-600 dark:text-neutral-400" fill="none" viewBox="0 0 16 16">            <path clip-rule="evenodd" d="M8 13.5a5.5 5.5 0 1 0 0-11a5.5 5.5 0 0 0 0 11M8 15A7 7 0 1 0 8 1a7 7 0 0 0 0 14m1-9.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0m-.25 3a.75.75 0 0 0-1.5 0V11a.75.75 0 0 0 1.5 0z" fill="currentColor" fill-rule="evenodd" />          </svg>          <div class="flex flex-col gap-0.5">            <p v-if="(content as ToastContentValue).title" class="text-sm font-medium text-neutral-900 dark:text-neutral-50">              {{ (content as ToastContentValue).title }}            </p>            <p v-if="(content as ToastContentValue).description" class="text-sm text-neutral-600 dark:text-neutral-400">              {{ (content as ToastContentValue).description }}            </p>          </div>        </div>      </template>    </ToastProvider>    <Button size="sm" variant="secondary" @click="show">      Show toast    </Button>  </div></template>

Global theme override

app.use(createHareUI({
  ui: {
    toast: {
      slots: { toast: 'rounded-xl shadow-lg', content: 'gap-2' },
    },
  },
}))

Styling Reference

ui keydata-slotElement
regiontoast-regionThe positioned landmark region toasts are teleported into
toasttoastEach toast's positioned, animated wrapper (Reka has no built-in equivalent; implemented directly)
contenttoast-contentWrapper for the title and description
indicatortoast-indicatorIcon/spinner container, rendered unless indicator is null
titletoast-titleThe toast title
descriptiontoast-descriptionThe toast description
closetoast-closeThe close button
actiontoast-action-buttonThe optional action button

Interactive States

  • [data-frontmost] — applied to the topmost visible toast
  • [data-index] — applied based on toast position in stack
  • [data-placement] — applied based on the region's placement
  • [data-entering] — applied on first paint of a new toast
  • [data-exiting] — applied while a closing toast plays its exit transition
  • [data-expanded] — applied to every toast while the stack is expanded
  • [data-hidden] — applied to toasts beyond maxVisibleToasts

API Reference

ToastProvider Props

PropTypeDefaultDescription
placement"top" | "bottom" | "bottom start" | "bottom end" | "top start" | "top end""bottom"Placement of the toast region.
gapnumberDEFAULT_GAPThe gap between toasts in pixels.
isExpandedbooleanfalseKeeps the stack in its expanded layout instead of expanding only on hover or keyboard focus. Forced expansion does not pause toast timers.
maxVisibleToastsnumber3, or the queue's own `maxVisibleToasts`The maximum number of toasts to display at a time (visual only).
hotkeystring[]DEFAULT_HOTKEYHotkey that focuses the toast region. Modifiers match KeyboardEvent boolean props, other keys match event.code. Pass [] to disable.
scaleFactornumberDEFAULT_SCALE_FACTORHow much each toast behind the front one scales down.
widthstring | numberDEFAULT_TOAST_WIDTHThe width of the toast region; numbers are pixels.
queueToastQueue<ToastContentValue>-Custom toast queue instance; defaults to the shared default queue used by toast().
ariaLabelstring"Notifications"Accessible label for the region landmark.
portalstring | boolean | HTMLElementtrueWhere the region is teleported. true teleports to body, false renders in place, a string or element teleports to that target.
uiComponentSlots<{ slots: { region: string[]; toast: string[]; content: string; indicator: string[]; title: string; description: string; close: string[]; action: string; }; variants: { placement: { bottom: { region: string; toast: string; }; 'bottom start': { region: string; toast: string; }; 'bottom end': { region: string; toast: string; }; top: { region: string; toast: string; }; 'top start': { region: string; toast: string; }; 'top end': { region: string; toast: string; }; }; variant: { default: {}; accent: { title: string; }; success: { title: string; indicator: string; }; warning: { title: string; indicator: string; }; danger: { title: string; indicator: string; }; }; }; defaultVariants: { placement: string; variant: string; }; }>-Per-slot class overrides, shared by every rendered toast.

ToastProvider Slots

SlotPropsDescription
toast{ content: ToastContentValue; close: () => void; }Replaces a toast's inner content (indicator, title, description, action, close button). The positioned, animated stack wrapper is still rendered by the provider; style it with the ui.toast slot override.

ToastQueue

Manages toast state outside any component, so toasts can be triggered from anywhere — including outside a component instance. Pass a custom instance to ToastProvider's queue prop to run an independent stack; otherwise every ToastProvider shares the module-level default queue used by toast().

Constructor Options

OptionTypeDefaultDescription
exitDurationnumber300How long a closing toast stays mounted for its exit animation (0 removes immediately)
maxVisibleToastsnumber3Maximum number of toasts to display at once (visual only)

Methods

MethodParametersReturnsDescription
add(content, options?: { timeout?, onClose? })stringAdds a toast, returns its key
update(key, content, options?: { timeout?, onClose? })booleanUpdates a toast in place, preserving its stack position; false if it no longer exists
close(key)voidCloses a toast by its key
clear()voidCloses every toast
pauseAll()voidPauses every toast's auto-dismiss timer
resumeAll()voidResumes every toast's auto-dismiss timer
visibleToasts—QueuedToast[]The currently rendered toasts, newest first (getter)

toast / useToast()

toast and useToast() are equivalent — useToast() just returns the toast singleton, provided for symmetry with other composables; toast state lives outside any component instance, so toast works from anywhere, including outside <script setup>.

import { toast } from '@hareui/vue'

// Basic toast (auto-dismisses after 4 seconds by default)
toast('Event has been created')

// Variant methods (also auto-dismiss after 4 seconds by default)
toast.success('File saved')
toast.info('New update available')
toast.warning('Please check your settings')
toast.danger('Something went wrong')

// With options. The returned id lets an action close its own toast.
const eventId = toast('Event has been created', {
  description: 'Your event has been scheduled for tomorrow',
  variant: 'default',
  timeout: 5000, // Custom timeout: 5 seconds
  onClose: () => console.log('Closed'),
  action: { label: 'View', onClick: () => toast.close(eventId) },
  indicator: h(CustomIcon),
})

// Update an existing toast in place (keeps its position in the stack).
// Options you omit are inherited, so pass `timeout` to start a countdown
// on a toast that was created persistent.
const id = toast('Saving…', { timeout: 0 })
toast.update(id, 'Saved', { variant: 'success', timeout: 4000 })

// Promise support (automatically shows a loading spinner). The loading toast
// updates in place when the promise settles — same toast, same stack
// position; the auto-dismiss countdown starts at that point.
toast.promise(uploadFile(), {
  loading: 'Uploading file...',
  success: data => `File ${data.filename} uploaded`,
  error: 'Failed to upload file',
})

// Manual loading state (persistent toast - no auto-dismiss)
const loadingId = toast('Creating event...', { isLoading: true, timeout: 0 })

// Later, update in place and start the auto-dismiss countdown
toast.update(loadingId, 'Event created', { variant: 'success', timeout: 4000 })

// Queue methods
toast.close(key)
toast.clear()
toast.pauseAll()
toast.resumeAll()

toast Options

OptionTypeDefaultDescription
descriptionVNodeChild-Optional description text
variant'default' | 'accent' | 'success' | 'warning' | 'danger''default'Visual variant
indicatorVNodeChild | null-Custom indicator icon (null hides it)
action{ label, onClick?, variant?, class? }-Action button
isLoadingbooleanfalseShows a spinner in place of the indicator
timeoutnumber4000Auto-dismiss timeout in milliseconds; 0 keeps the toast open
onClose() => void-Called when the toast is dismissed, as its exit animation starts

toast.promise Options

OptionTypeDefaultDescription
loadingVNodeChild-Message shown while the promise is pending
successVNodeChild | ((data: T) => VNodeChild)-Message shown on success
errorVNodeChild | ((error: Error) => VNodeChild)-Message shown on error

Accessibility

Each toast implements the WAI-ARIA alertdialog pattern, grouped inside a labeled landmark region:

  • Landmark region: toasts render in a labeled landmark reachable with F6; Alt + T moves focus straight to it. The hotkey is configurable via the hotkey prop and can be disabled by passing an empty array; the region label can be overridden with ariaLabel
  • Keyboard: Tab reaches the controls of visible toasts, Escape collapses the expanded stack, and dismissing a focused toast moves focus to the nearest remaining toast
  • Screen readers: each toast uses role="alertdialog" with its title and description linked; new toasts are announced automatically
  • Timers: auto-dismiss pauses while the stack is hovered or focused, and while the page is in a background tab

On this page

No Headings