Skip to content
Kappa
Toast
@dicehub/kappav0.4.2

Toast

Brief notifications for action results, background tasks, and recoverable errors.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();
</script>

<template>
  <Button variant="outline" @click="toaster.success({
    title: 'Changes saved',
    description: 'Your project settings are up to date.',
  })">Save changes</Button>
  <Toaster :toaster="toaster" />
</template>

Installation

Toast uses the existing Kappa package and Ark UI Vue. No extra dependency is required.

Barrel

import { Toast, Toaster, createToaster } from "@dicehub/kappa";

Granular

import { Toast, Toaster, createToaster } from "@dicehub/kappa/components/toast";

For a TypeScript service that does not render Vue parts, use the store-only entrypoint:

import { createToaster } from "@dicehub/kappa/components/toast/store";

Usage

Create a store with createToaster(), pass it to one Toaster, then call the store from your event handlers. Share that instance with the controls that need it. The host provides a ready-to-use layout, not an application context.

For server-rendered applications, create the store per application or request. Do not share user notifications through a server module singleton. Keep the host mounted across route changes if notifications must persist, and call remove() when disposing a request-scoped or temporary store.

Keep each store identity stable. To change placement, create a new store and remount its host. Use no more than one host per placement in a document: Ark uses placement-based region IDs. This page shares one host and clears the previous example when you switch examples.

Kappa uses bottom-end, a compact overlapping stack, an 8 px gap, and five seconds for non-loading messages. Toaster.limit displays the newest three. Older messages stay mounted but hidden and inert; their Ark timers continue unless the group is paused. They can return when a newer message closes before they expire. The default renderer includes a close button unless closable: false.

The visible limit is separate from Ark's queue: createToaster defaultsmax to Infinity, so new messages appear immediately. Set a finite max to opt into Ark's priority queue, or setoverlap: false to keep the visible stack expanded. Timers, promises, updates, focus, and dismissal remain managed by Ark UI.

Composition

Typical part hierarchy
Toaster (store + notification region)
└── Toast.Root (one per notification)
    ├── Toast.Indicator
    ├── Toast.Title
    ├── Toast.Description
    ├── Toast.ActionTrigger
    └── Toast.CloseTrigger

Toaster supplies the group and per-toast context. Its default slot can replace the complete notification. Toast.Root, Toast.Title,Toast.Description, Toast.ActionTrigger, andToast.CloseTrigger preserve the primitive semantics.Toast.Indicator adds the status icon or a decorative Kappa Loader.

Custom layouts must use Title and Description for options they render, so the generated accessible references resolve. Keep action callbacks in toast.action; the ActionTrigger invokes them and then dismisses the notification.

Examples

Types

Success, error, warning, and information messages share a quiet surface. The icon identifies the status without relying on color alone.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();

const types = ["success", "error", "warning", "info"];
</script>

<template>
  <Button v-for="type in types" :key="type" variant="outline"
    @click="toaster.create({ type, title: type, description: 'Notification details.' })">
    {{ type }}
  </Button>
  <Toaster :toaster="toaster" />
</template>

Title and Description

Use a title, a description, or both. Set closable to false to hide the default close button; Escape still dismisses a focused notification.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();
</script>

<template>
  <Button @click="toaster.create({ title: 'Settings saved' })">Title only</Button>
  <Button @click="toaster.create({ description: 'Your changes are available to the team.' })">Description only</Button>
  <Button @click="toaster.create({ title: 'Automatic notice', closable: false })">Without close button</Button>
  <Toaster :toaster="toaster" />
</template>

Action

Offer one short action. This persistent notification lets the user undo a local change. The action callback runs before Ark dismisses the toast.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";
import { ref } from "vue";

const toaster = createToaster();

const removed = ref(false);
function removeItem() {
  removed.value = true;
  toaster.create({
    title: "Item removed",
    description: "You can undo this change.",
    duration: Infinity,
    action: { label: "Undo", onClick: () => { removed.value = false; } },
  });
}
</script>

<template>
  <Button @click="removeItem">Remove item</Button>
  <span>{{ removed ? 'Item removed' : 'Item available' }}</span>
  <Toaster :toaster="toaster" />
</template>

Promise

One notification follows an operation from loading to success or error. Use Finish upload or Fail upload to resolve this local example; it sends no files.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();

let task;
function startUpload() {
  task = Promise.withResolvers();
  toaster.promise(task.promise, {
    loading: { title: "Uploading geometry" },
    success: (file) => ({ title: "Upload complete", description: file + " is ready." }),
    error: { title: "Upload failed", description: "No files were changed. Try again." },
  });
}
</script>

<template>
  <Button @click="startUpload">Start upload</Button>
  <Button @click="task?.resolve('geometry.step')">Finish upload</Button>
  <Button @click="task?.reject(new Error('Connection lost'))">Fail upload</Button>
  <Toaster :toaster="toaster" />
</template>

Update in Place

Keep the ID returned by create to update the existing notification. Changing loading to success starts the normal dismissal timer.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();

let id;
function startExport() {
  id = toaster.create({ title: "Preparing export", type: "loading" });
}
function completeExport() {
  if (id) toaster.update(id, { title: "Export ready", type: "success", duration: 5000 });
}
</script>

<template>
  <Button @click="startExport">Start export</Button>
  <Button @click="completeExport">Complete export</Button>
  <Toaster :toaster="toaster" />
</template>

Stack and Queue

The newest three notifications form a compact stack that expands on hover or focus. Add messages one at a time or in a burst. Older messages remain mounted but hidden and inert until space is available or their timers expire. Always expanded uses overlap: false. Queue overflow explicitly sets max: 3 instead of delaying new messages by default.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster({ duration: Infinity });

let number = 0;
function addNotifications(count = 1) {
  for (let index = 0; index < count; index++) {
    toaster.create({ title: "Notification " + ++number });
  }
}
</script>

<template>
  <Button @click="addNotifications()">Add notification</Button>
  <Button @click="addNotifications(6)">Add six notifications</Button>
  <Button @click="toaster.remove()">Clear all</Button>
  <Toaster :toaster="toaster" />
</template>

Duration and Pause

The default duration is five seconds. Hovering or focusing the group pauses timers. Leaving the browser tab also pauses them. Infinity keeps a notification open until dismissed.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();
</script>

<template>
  <Button @click="toaster.create({ title: 'Link copied', duration: 2000 })">Two seconds</Button>
  <Button @click="toaster.create({ title: 'Review required', duration: Infinity })">Persistent</Button>
  <Toaster :toaster="toaster" />
</template>

Placement

Choose one of six placements when creating the store. Mount each store once, with at most one Toaster per placement in a document. This page replaces the active example when you select a new placement.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster({ placement: "top-end", offsets: "1rem" });
</script>

<template>
  <Button @click="toaster.info({ title: 'Position preview' })">Show notification</Button>
  <Toaster :toaster="toaster" />
</template>

Right-to-left

DirectionProvider supplies the locale to Ark UI. Logical placement and the content order follow the reading direction.

<script setup>
import { DirectionProvider } from "@dicehub/kappa/components/direction-provider";
import { Button } from "@dicehub/kappa/components/button";
import { Toaster, createToaster } from "@dicehub/kappa/components/toast";
const toaster = createToaster({ placement: "bottom-end" });
</script>

<template>
  <DirectionProvider locale="ar">
    <Button @click="toaster.success({ title: 'تم الحفظ', description: 'تم تحديث إعدادات المشروع.' })">Show notification</Button>
    <Toaster :toaster="toaster" />
  </DirectionProvider>
</template>

Custom Composition

The default slot receives the toast options. Use the named parts to keep primitive semantics, and compose an existing Kappa Button through as-child. Toast.Context exposes the live notification state.

<script setup>
import { Button } from "@dicehub/kappa/components/button";
import { Toast, Toaster, createToaster } from "@dicehub/kappa/components/toast";

const toaster = createToaster();
function shareProject() {
  toaster.create({
    title: "Project shared",
    description: "The team can now access this project.",
    action: { label: "View project", onClick: () => console.log("Open project") },
  });
}
</script>

<template>
  <Button @click="shareProject">Share project</Button>
  <Toaster v-slot="toast" :toaster="toaster">
    <Toast.Root>
      <Toast.Title>{{ toast.title }}</Toast.Title>
      <Toast.Description>{{ toast.description }}</Toast.Description>
      <Toast.ActionTrigger as-child>
        <Button size="xs" variant="outline">{{ toast.action.label }}</Button>
      </Toast.ActionTrigger>
      <Toast.CloseTrigger />
    </Toast.Root>
  </Toaster>
</template>

Accessibility

  • The notification region uses polite live announcements. A toast does not take focus when it appears.
  • Alt + T moves focus to the group. Use Tab to reach a notification, action, or close button.
  • Escape dismisses a focused toast. Group focus and pointer hover pause timers.
  • Use persistent messages for important actions. Do not use a transient toast as the only location for a required error or instruction.
  • Use visible status text as well as icons and color. Loading icons are decorative and do not add another live region.
  • Provide a translated closeLabel for the default host, or an aria-label on a custom CloseTrigger.
  • DirectionProvider controls text direction. Surfaces use shared theme tokens, and transitions respect reduced motion.

Use Banner for persistent in-page notices andDialog for decisions that must interrupt a workflow. Notifications outside a modal's focus scope must not contain actions required to complete that modal.

API Reference

Toaster

PropertyTypeDefaultDescription
toasterCreateToasterReturnRequiredStore from createToaster. Keep its identity stable; remount the host if replacing it.
limitnumber3Maximum displayed notifications, newest first. Older notifications stay managed but hidden and inert. Infinity displays all.
teleportbooleantrueMove the region to its target after mount. Set false for an explicit local environment.
teleportTostring | HTMLElement"body"Teleport target. It must exist when the host mounts.
closeLabelstring"Dismiss notification"Accessible label for the default close control.
default slot(toast: ToastOptions) => VNodeChildKappa contentReplace the entire notification, inside the per-toast context.

createToaster

PropertyTypeDefaultDescription
placementToastPlacement"bottom-end"Logical placement for the group.
maxnumberInfinityOptional Ark queue cap. Set a finite value to queue new messages after this many are mounted. Toaster.limit controls the visible stack separately.
gapnumber8Gap between notifications in pixels.
durationnumber5000Default duration in milliseconds. Loading notifications persist.
overlapbooleantrueUse a compact stack; hover and focus expand it. Set false to keep notifications expanded.
offsetsstring | edge object"1rem"Distance from each viewport edge, including safe-area handling.
removeDelaynumber200Delay before removal, allowing the exit transition to finish.
pauseOnPageIdlebooleantruePause timers while the browser tab is hidden.
hotkeystring[]["altKey", "KeyT"]Shortcut for moving focus to the notification group.

Toast Options

PropertyTypeDefaultDescription
title / descriptionVNodeChild—Notification content. The default renderer preserves strings and Vue VNodes; it never parses HTML.
typeToastType"info"Use success, error, warning, info, or loading.
idstringGeneratedStable ID for updates. Keep IDs unique across stores.
durationnumberStore defaultPer-notification duration; Infinity persists until dismissal.
closablebooleanShown by defaultThe Kappa default renderer hides its close button only when false.
actionToastActionOptions—A label and onClick callback. The action dismisses the notification.
onStatusChange(details) => void—Receives visible, dismissing, and unmounted status changes.
priority / metanumber / objectArk defaultsPriority affects queued messages; meta stores application data.

Store Methods

MethodDescription
create(options)Create a message and return its ID.
success / error / warning / info / loadingCreate a message with the corresponding type.
update(id, options)Update a notification in place.
promise(operation, options, shared?)Track promise states. Returns an ID and unwrap() to access the result.
dismiss(id?)Dismiss visible notifications with an exit transition. The queue can then reveal more.
remove(id?)Remove immediately. Without an ID, clear both visible and queued notifications.
pause(id?) / resume(id?)Control automatic dismissal timers.
expand() / collapse()Control an overlapping stack.
getCount() / getVisibleToasts()Read Ark's mounted notifications, including older items hidden by Toaster.limit; queued items are excluded.
isVisible(id) / isDismissed(id) / subscribe(callback)Observe the existing Ark store.

Parts and Styling

Root, Title, Description, ActionTrigger, and CloseTrigger accept asChildand forward attributes to the primitive element. Context exposes the live toast API. Indicator supports a custom default slot and remains decorative.

Set --kappa-toast-width on Toaster to change its default 22 rem width. The width is clamped to the viewport and configured offsets. Shared control, text, border, focus, status, and popover-shadow tokens apply in both themes. Ark's inline geometry variables remain internal; Kappa consumes them for position, height, opacity, and stack order. Overlapping siblings use a positive scale derived from Ark's index, with an offset correction that keeps each exposed edge 8 px apart even when toast heights differ. Measured heights follow Ark's display indexes instead of measurement arrival order, so a synchronous burst keeps the newest message at the viewport edge. Kappa also corrects Ark 5.39's double conversion of RTL group insets; start and end remain logical.

Exports

Toast, Toaster, createToaster, all named parts, useToastContext, and toastAnatomy are available from the main package and the toast subpath. Public types include ToasterProps,ToasterSlots, CreateToasterProps, CreateToasterReturn,ToastOptions, ToastPlacement, ToastType,ToastPromiseOptions, and the part, context, action, and status types.