Skip to content
kitn AI/UI

Toast

toast() · kai-toast-region

Raise a transient notification from anywhere with one call — toast('Copied to clipboard'). The first toast lazily mounts a single viewport overlay on document.body; you never place an element by hand.

  • Imperative API
  • Shadow DOM overlay
  • Undo actions
  • Auto-stack (max 3)
  • Pause on hover

Import toast and call it. No element, no setup:

import { toast } from '@kitn.ai/ui/elements';
toast('Copied to clipboard');
toast.success('Saved');

The first call creates one <kai-toast-region> on document.body and binds it to a shared store, so every later toast() lands in the same stack. toast is also exported from the package root (@kitn.ai/ui) if you’d rather import it alongside the SolidJS layer.

toast(message, options?) returns a { id, dismiss, update } handle. Two named helpers cover the common cases:

import { toast } from '@kitn.ai/ui/elements';
toast('Copied to clipboard'); // neutral
toast.success('Profile updated'); // emerald check
toast.dismiss(id); // remove one toast by id
toast.clear(); // remove all
CallWhat it does
toast(message, opts?)Raise a neutral toast. Returns a ToastHandle.
toast.success(message, opts?)Same, with the success (check) variant.
toast.dismiss(id)Remove the toast with that id.
toast.clear()Remove every active toast.

Options — the second argument to toast():

OptionTypeDefaultNotes
idstringautoPass an existing id to update that toast in place instead of stacking a new one.
variant'neutral' | 'success''neutral'toast.success(…) sets this for you.
action{ label, onAction }Adds a button. Return false from onAction to keep the toast open.
durationnumber (ms)5000Auto-dismiss delay. 0 is sticky. With an action, the floor is 7000 so there’s time to act.
dismissiblebooleantrueWhether the × close button shows.

The returned handle controls the toast after it’s shown:

const t = toast('Uploading…', { duration: 0 }); // sticky while the work runs
// later…
t.update({ message: 'Uploaded', variant: 'success', duration: 2000 });
// or
t.dismiss();

A confirmation with the emerald check. Auto-dismisses after 5 seconds.

import { toast } from '@kitn.ai/ui/elements';
toast.success('Copied to clipboard');

Add an action to offer a one-tap reversal. The toast stays up at least 7 seconds when it carries an action, so the user has time to react:

import { toast } from '@kitn.ai/ui/elements';
function deleteItem(item) {
remove(item);
toast('Item deleted', {
action: { label: 'Undo', onAction: () => restore(item) },
});
}

Returning false from onAction keeps the toast open — useful when the action kicks off follow-up work you want to report in the same toast.

Raise several toasts and they stack newest-on-top. The region shows up to max at once (3 by default); the rest queue and promote as slots free up:

import { toast } from '@kitn.ai/ui/elements';
toast('Connecting…');
toast('Syncing files');
toast.success('All set');

Set duration: 0 to keep a toast until you dismiss it — for ongoing work whose end you’ll signal yourself. Update it in place when the work finishes:

import { toast } from '@kitn.ai/ui/elements';
const t = toast('Generating report…', { duration: 0 });
await buildReport();
t.update({ message: 'Report ready', variant: 'success', duration: 2000 });

toast() is the path you’ll reach for. The imperative API is backed by <kai-toast-region> — a real kai-* web component with its own Shadow DOM and the shared kit stylesheet, so it’s viewport-positioned and fully themed, never a bare div.

You only touch the element directly to own the toast list yourself (e.g. driving it from your own state) or to change where the stack anchors. Set toasts in JavaScript (arrays can’t be HTML attributes) and listen for kai-dismiss / kai-action:

<kai-toast-region position="bottom-right" max="2"></kai-toast-region>
<script type="module">
import '@kitn.ai/ui/elements';
await customElements.whenDefined('kai-toast-region');
const region = document.querySelector('kai-toast-region');
region.toasts = [
{ id: 't1', message: 'Saved', variant: 'success' },
{ id: 't2', message: 'Item deleted', action: { label: 'Undo', onAction: () => restore() } },
];
region.addEventListener('kai-dismiss', (e) => console.log('left:', e.detail.id, e.detail.reason));
region.addEventListener('kai-action', (e) => console.log('action:', e.detail.id, e.detail.label));
</script>

A toast item has the same shape as a toast() call:

FieldTypeNotes
idstringStable id; echoed in kai-dismiss / kai-action.
messagestringThe text.
variant'neutral' | 'success'Defaults to 'neutral'.
action{ label, onAction }Optional action button.
durationnumber (ms)0 = sticky; defaults to 5000 (floor 7000 with an action).
dismissiblebooleanShow the × button. Defaults to true.
  • Auto-dismiss after duration (5s default), paused while hovered so it doesn’t vanish mid-read.
  • Stack cap at max (3); overflow queues and promotes as toasts leave.
  • Announced via aria-live="polite" / role="region" — screen readers hear new toasts without losing focus.
  • Reduced motion — the slide/fade is suppressed under prefers-reduced-motion.

These apply to the <kai-toast-region> element.

PropertyTypeDefaultNotes
toasts[]The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update.
position'top-center'Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, …
max3Max simultaneously-visible toasts; the rest queue. Defaults to `3`.
stack'expanded'Stacking: 'expanded' (default, full column) | 'collapsed' (Sonner-style pile that expands on hover/focus). Attribute: stack.
appearance'pill'Default appearance for this region's toasts: `'pill'` (default, compact) | `'card'` (richer, with a description line). A per-toast `appearance` wins. Attribute: `appearance`.
inversefalseDefault high-contrast inverse treatment for this region's toasts. A per-toast `inverse` wins. Off by default. Attribute: `inverse`.
targetContainer element to anchor this region to (JS property). Set by the store for a scoped region; unset = the global viewport region.
EventDetailNotes
A toast's action button was pressed.
A toast left the stack. `reason` is `'timeout' | 'close' | 'action'`.

This element wraps these SolidJS components — reach for them directly when you need finer control than the props expose.

ToastRegion