Toast
A stackable notification, triggered imperatively via useToast().
Usage
Wrap your root app.vue in <SApp> once (already done by this docs site's
shell),
and place <SToast /> inside it, wrapped in <ClientOnly>. Then push toasts
from anywhere with useToast():
<!-- app.vue -->
<SApp>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<ClientOnly>
<SToast />
</ClientOnly>
</SApp>
SApp establishes Reka UI's ToastProvider around your whole app - it must
be mounted exactly once, at the true app root, not per-layout or per-page.
Mounting it more than once would register multiple providers for the same
toast state, causing every toast to render once per instance.
SToast is the part that actually renders toasts - it does real DOM
measurement and CSS-driven animation, so it needs <ClientOnly> around it.
SApp itself doesn't: it only establishes context, so it renders on the
server like any other component.
<script setup lang="ts">
const { add } = useToast()
function save() {
add({ title: 'Saved', description: 'Your changes have been saved.' })
}
</script>
<template>
<SButton @click="save">
Show toast
</SButton>
</template>
Colors
color accepts any registered semantic role and applies its soft background,
matching the color treatment used by SAlert. The built-in success, danger,
warning, and info intents select matching default icons. Other roles do not
guess an icon; set icon explicitly when needed. With no color, the toast keeps
a neutral surface and an explicit icon uses a neutral color:
<SButton @click="add({ color: 'success', title: 'Saved' })">
Show success toast
</SButton>
Position and stacking
Set position on the single <SToast /> renderer to choose any screen corner
or center edge. The default is bottom-right. Multiple toasts layer into a
compact stack: each new toast slides in at the front and pushes the older
ones back, with up to three cards visible. The stack fans open while the
pointer or keyboard focus is inside it; set expand to show every toast
separately instead. Click a position a few times to see the stack build up,
then hover it:
<ClientOnly>
<SToast position="top-center" expand />
</ClientOnly>
Timing and count
duration sets the default time in milliseconds that each toast stays visible;
the duration passed to add() overrides it for that toast. max limits how
many are visible at once. When the limit is reached, the oldest toast is
removed so newer notifications can appear without building a hidden backlog.
<ClientOnly>
<SToast :duration="8000" :max="3" />
</ClientOnly>
Accessibility
Toast renders Reka UI's Toast primitive, so the accessibility semantics
come from there rather than being reimplemented here: each toast announces
itself via aria-live without stealing focus from whatever the user was
doing, a hover or focus inside it pauses its own dismiss timer (resuming
once it's left), and on touch devices it can be swiped away.
useToast()
| Method | Description |
|---|---|
add(toast) | Pushes a toast ({ title?, description?, duration?, color?, icon? }), returns its id |
remove(id) | Dismisses a toast by id |
Custom :ui
To see exactly what you'd be overriding - the current default classes for every slot and variant - here's the toast's own theme file:
Props (SToast)
| Prop | Type | Default |
|---|---|---|
ui | Partial<Record<'viewport' | 'root' | 'title' | 'description' | 'icon' | 'close', string | object>> | - |
position | 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right' | 'bottom-right' |
expand | boolean | false |
duration | number | 5000 |
max | number | 5 |