Skip to content

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():

vue-html
<!-- 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.

vue
<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:

vue-html
<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:

vue-html
<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.

vue-html
<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()

MethodDescription
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:

Theme source "toast" not found

Props (SToast)

PropTypeDefault
uiPartial<Record<'viewport' | 'root' | 'title' | 'description' | 'icon' | 'close', string | object>>-
position'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right''bottom-right'
expandbooleanfalse
durationnumber5000
maxnumber5
Edit this page