elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Overlays / Toast

Toast

Short notices that stack at an edge of the screen, pause on hover and close on their own or with a swipe.

Types

A title, an optional description, and an icon per type. Hover the stack to fan it out; swipe a toast to dismiss it.

Code
toast("Event has been created");
toast("Event has been created", { description: "Monday, January 3rd at 6:00pm" });
toast.success("Event has been created");
toast.info("Be at the area 10 minutes before the event time");
toast.warning("Event start time cannot be earlier than 8am");
toast.error("Event has not been created");
toast.loading("Loading data");

Usage

import { Toaster, Toast } from "@elements/ui/toast";
import { toast } from "@elements/ui/toast";

<!-- once, in the app shell -->
<Toaster richColors />

<!-- anywhere in browser code -->
<Button onclick={() => toast.success("Event created", {
  description: "Sunday, December 3 at 9:00 AM",
  action: { label: "Undo", onClick: () => undo() },
})}>Create event</Button>

<Button onclick={() => toast.promise(save(form), {
  loading: "Saving...",
  success: (doc) => `Saved ${doc.name}`,
  error: "Could not save",
})}>Save</Button>

<!-- declarative: open is two-way -->
<Toast open={state.saved} title="Saved" type="success" />

<!-- anchored: next to the button instead of in the stack -->
<Button onclick={(e) => toast("Copied", {
  anchor: e.currentTarget as Element,
  positioner: { side: "top", sideOffset: 8, arrow: true },
})}>Copy</Button>

Examples

Action and cancel

The action closes the toast after it runs, unless its onClick calls preventDefault(). Cancel runs and closes.

Code
toast("Event has been created", {
  action: { label: "Undo", onClick: () => undo(), altText: "Undo from the events page" },
});

toast("Delete draft?", {
  cancel: { label: "Keep", onClick: () => keep() },
  action: { label: "Delete", onClick: () => remove() },
});

Promise

A loading toast that turns into the result or the error when the promise settles. The icon box and the width never change.

Code
toast.promise(save(), {
  loading: "Saving...",
  success: (doc) => `${doc.name} has been saved`,
  error: "Could not save",
});

// a function, an object result, and unwrap()
let { unwrap } = toast.promise(() => upload(file), {
  loading: "Uploading...",
  success: (r) => ({ message: "Uploaded", description: r.url }),
  error: (err) => `Upload failed: ${err.message}`,
});
await unwrap();

Position

Six positions. Swipe directions follow the position: bottom-right swipes down or right.

Code
<Toaster position="top-center" />

// or per toast
toast("Moved", { position: "top-left" });

Options

Toaster attributes: keep the stack expanded, status colors, a close button on each toast, inverse colors, and a light or dark theme whatever the page's mode.

Code
<Toaster expand richColors closeButton invert duration={4000} visibleToasts={3} />

// dark toasts on a light page, light on a dark one (or "system")
<Toaster theme="dark" />

Lifetime and dismissal

A toast that stays, one that cannot be dismissed by the user, callbacks, and dismissing everything.

Code
let id = toast.loading("Uploading...");
toast.success("Uploaded", { id });                 // same id: updates in place

toast("Stays until closed", { duration: Infinity });
toast("Cannot be swiped away", { dismissible: false, duration: Infinity });
toast("Connection lost", { important: true });     // assertive announcement
toast("Logged", { onDismiss: (t) => log(t), onAutoClose: (t) => log(t) });
toast.dismiss(id);
toast.dismiss();                                   // every toast

Declarative

<Toast open> adds a toast while open is true and writes false back when it closes itself.

open: false · changes:
Code
<Button onclick={() => state.saved = true}>Save</Button>
<Toast open={state.saved} type="success" title="Saved" action={{ label: "View" }}
       description="Your changes are live." onOpenChange={(open) => log(open)} />

Uncontrolled

A literal open makes a Toast uncontrolled: it shows when it renders and closes itself, so a list of them is a list of toasts.

Code
<Button onclick={() => state.batch.push({ id: state.batch.length + 1 })}>Add a toast</Button>

<!-- A literal open: each one shows when it renders and closes itself. -->
<Toast e:for={b of state.batch} open title={`Upload ${b.id} finished`} closeButton />

Anchored

A toast with an anchor sits next to that element instead of in the stack, with an optional arrow. Pressed again, the same id updates it in place.

Code
<Button onclick={(e) => onCopy(e)}>Copy</Button>

function onCopy(e: MouseEvent): void {
  navigator.clipboard.writeText(code);
  toast("Copied", {
    id: "copied",                                   // pressed again: the same toast
    anchor: e.currentTarget as Element,
    positioner: { side: "top", sideOffset: 8, arrow: true },
    duration: 1500,
  });
}

Updates and data

Showing a toast again with the same id updates it in place and replays data-updated, which this demo uses for a pulse. data carries your own values to a function description.

Code
interface Inbox { unread: number }

// The same id updates the toast in place; data-updated replays each time,
// so CSS can pulse it. A function description is given the toast.
toast("New messages", {
  id: "inbox",
  data: { unread: state.unread },
  description: (t) => `${(t.data as Inbox).unread} unread`,
  class: "pulse-on-update",
});

toast.update("inbox", (t) => ({ title: "All caught up", type: "success", data: { unread: 0 } }));

Several toasters

A Toaster with an id takes only the toasts sent to it with toasterId.

Code
<Toaster />
<Toaster id="side" position="top-left" offset={{ top: 72, left: 24 }} gap={8} expand
         swipeDirections={["left"]} icons={{ info: "bell" }}
         toastOptions={{ class: "side-toast" }} />

toast("Goes to the side toaster", { toasterId: "side" });

Custom content

toast.custom() places your own node in a toast, inside the chrome or headless. A function title re-renders when what it reads changes.

Code
// Your own node, inside the toast's chrome
toast.custom(() => card("A custom toast with default styling"), { unstyled: false });

// Headless: no chrome; the node owns its look and its buttons
toast.custom((id) => {
  let box = document.createElement("div");
  box.className = "my-toast";
  // ... title, text, and a button that calls toast.dismiss(id)
  return box;
});

// A function title or description re-renders when what it reads changes
toast(() => `${state.files} files uploaded`, { description: () => `${state.files * 2} MB` });

Styling

Inline style and a class on every part of one toast, and styles on its buttons. A Toaster takes style, offset and toastOptions for all of its toasts.

Code
toast("Styled", {
  style: "border-style: dashed",
  classes: { title: "my-title", description: "my-description", actionButton: "my-action" },
  action: { label: "Open", style: "font-style: italic" },
  cancel: { label: "Later", style: "text-decoration: underline" },
});

<Toaster style="--my-mark: 1" offset={16} mobileOffset={{ bottom: "16px" }}
         toastOptions={{ style: "...", classes: { closeButton: "my-close" } }} />

Over a dialog

The region is in the top layer and is raised when a toast arrives, so a toast shown from a modal dialog is drawn above it and announced. The hotkey, Tab, hover, clicks and swipes reach it while the dialog stays open.

Code
<Dialog title="Settings">
  <Button slot="trigger">Open dialog</Button>
  <Button onclick={() => toast("Saved from a dialog")}>Save</Button>
</Dialog>
Reference Toast in the manual Every part, attribute, slot, key and data attribute.
Prev SheetNext Tooltip
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026