oriUI

useToast

A headless toast queue — an imperative notification store you call from anywhere (toast('Saved')) and render once near the app root. The behavior is a framework-agnostic engine (a plain array + a Set of listeners + auto-dismiss timers) projected into the host framework's reactivity (in Vue, a reactive array). It is a client-side singleton — every useToast() call shares the one queue, so a push from anywhere reaches the single renderer.

This is the Vue binding over a framework-agnostic queue that ships inside it (not from the core barrel, so an app without toasts never loads it), and the styled OriToaster + OriToast render the queue. Unlike the roving / machine composables, a toast queue has no ARIA prop-getters — the live-region roles live on the styled OriToast (role="status" / role="alert" by color).

Import

ts
import { useToast } from '@oriui/headless/vue'

@oriui/vue re-exports the same useToast, so import { useToast } from '@oriui/vue' is equivalent (and shares the one queue).

Options

toast(options) (and the severity shortcuts) take either a string (its text) or a ToastOptions object:

OptionTypeDefaultDescription
textstring—Body message. (toast('hi') is shorthand for toast({ text: 'hi' }).)
titlestring—Optional bold heading above the text.
colorToastColor—Semantic role — drives the accent and the live-region assertiveness on OriToast.
durationnumber4000Auto-dismiss delay in ms; 0 keeps the toast until it is dismissed.
closableboolean—Show a dismiss button. Left unset by the queue, so the renderer's own default applies — except for a toast with duration: 0, which opts itself in because nothing else could dismiss it.
action{ label: string; onClick: () => void }—One action button, such as Undo. Your renderer runs onClick and then dismiss(id).
iconstring—SVG path for a leading icon.

ToastColor is 'primary' | 'secondary' | 'surface' | 'background' | 'success' | 'warning' | 'danger' | 'info'.

Returns

PropertyTypeDescription
toastsToastItem[] (reactive)The live queue the renderer iterates. ToastItem is ToastOptions + a numeric id.
toast(options)(options: ToastOptions | string) => numberPush a toast; returns its numeric id (pass to dismiss).
success / error / warning / info(options: ToastOptions | string) => numberSeverity shortcuts — set color to success / danger / warning / info (an explicit color in the options still wins).
dismiss(id)(id: number) => voidRemove a toast (and cancel its timer); a no-op for an unknown id.
clear()() => voidEmpty the queue and cancel every timer.
pause() / resume()() => voidStop every countdown, then restart each with the time it had left. Call them from your renderer (see below).

Usage

Call toast() from anywhere, and render the queue once. The styled OriToaster does the rendering for you; to build your own, iterate toasts:

vue
<!-- MyToaster.vue — render the queue (what OriToaster wraps) -->
<script setup lang="ts">
import { useToast } from '@oriui/headless/vue'

const { toasts, dismiss, pause, resume } = useToast()
</script>

<template>
    <!-- A live region from mount. OriToaster also pauses for focus and a hidden page. -->
    <div class="my-toaster" aria-live="polite" @pointerenter="pause" @pointerleave="resume">
        <div v-for="t in toasts" :key="t.id" :role="t.color === 'danger' ? 'alert' : 'status'">
            {{ t.text }}
            <button v-if="t.action" @click="(dismiss(t.id), t.action.onClick())">{{ t.action.label }}</button>
            <button v-if="t.closable" @click="dismiss(t.id)">×</button>
        </div>
    </div>
</template>
ts
// anywhere — a component, a store, an interceptor
import { useToast } from '@oriui/headless/vue'

const { toast, success, error } = useToast()
success('Saved')
error({ title: 'Upload failed', text: 'Try again', duration: 0 }) // 0 = stay until dismissed

Accessibility

The queue carries no markup, so a renderer of your own has to do what OriToaster does:

  • Render the container empty on mount with aria-live="polite" and insert toasts into it; give each toast role="alert" for color="danger" and role="status" otherwise.
  • Call pause() while the pointer or focus is inside the container or the page is hidden, and resume() when none of these holds any more (WCAG 2.2.1). Without it a toast can vanish while someone reads it or tabs to its action.
  • Make the container a labeled role="region" with tabindex="-1" and a hotkey that focuses it, so keyboard users can reach the buttons.

See also

  • @oriui/headless — the framework-agnostic layer (the queue engine ships from the ./vue adapter, not the core barrel).
  • Toast — the styled OriToast + OriToaster (positioning, transitions, roles).