Skip to main content
WireKit
Copy for LLM

Toast

Event-driven notifications that appear temporarily to confirm actions or surface warnings. Toasts auto-dismiss after a configurable duration and stack vertically in a fixed corner of the viewport.

Basic Usage

Mount the toast region once per layout (typically in your app shell):

{{-- In your layout file --}}
<x-wirekit::toast-region />

The region listens on window, so anything that dispatches the event reaches it — a Livewire component after the write succeeds, or Alpine's $dispatch for a purely client-side action.

Most toasts report the outcome of something the server did, so that is the shape shown here: the button calls a Livewire action, and the toast is dispatched after the save returns. Nothing in the markup decides what the toast says.

Trigger a toast

Variants

Four semantic variants control color, icon, and ARIA behavior. Click each button to see the toast:

Toast variants

Position

Control where toasts appear on the screen:

<x-wirekit::toast-region position="top-right" />    {{-- default --}}
<x-wirekit::toast-region position="top-left" />
<x-wirekit::toast-region position="top-center" />
<x-wirekit::toast-region position="bottom-right" />
<x-wirekit::toast-region position="bottom-left" />
<x-wirekit::toast-region position="bottom-center" />

Each region below uses a scoped name prop so it only listens on its own channel — avoiding cross-talk with other regions on the same page:

Top-left position
Bottom-right position

Duration

Toasts auto-dismiss after 5 seconds by default. Override per-region or per-toast:

{{-- All toasts in this region dismiss after 3 seconds --}}
<x-wirekit::toast-region :duration="3000" />
// Per-toast override (0 = persistent, must be manually dismissed)
$dispatch('wirekit-toast', {
    title: 'Permanent',
    message: 'This toast stays until dismissed.',
    duration: 0
})

Queue Limit

Limit how many toasts are visible at once. Oldest toasts are removed when the limit is reached:

<x-wirekit::toast-region :max="3" />

Scoped Regions

By default, all toast regions listen on the global wirekit-toast event. When you have multiple regions (e.g. one for system notifications, one for form feedback), use the name prop to scope each region to its own event channel. This prevents duplicate toasts appearing in every region.

{{-- System notifications — listens on 'wirekit-toast-system' --}}
<x-wirekit::toast-region name="system" position="top-right" />

{{-- Form feedback — listens on 'wirekit-toast-form' --}}
<x-wirekit::toast-region name="form" position="bottom-right" />

Dispatch to a specific region by targeting its scoped event name:

// Only appears in the "form" region
$dispatch('wirekit-toast-form', {
    variant: 'success',
    title: 'Saved',
    message: 'Your changes were saved.'
})

// Only appears in the "system" region
$dispatch('wirekit-toast-system', {
    variant: 'warning',
    title: 'Maintenance',
    message: 'Scheduled downtime in 30 minutes.'
})

Regions without a name prop continue to listen on the default wirekit-toast event, so existing code works unchanged.

eventScope — DOM-containment filter (alternative to name)

When you can't (or don't want to) coordinate distinct event names — for example because every region on the page emits the same wirekit-toast event but each region should only handle events from its own portion of the DOM — set eventScope to a CSS selector. The region then ignores any event whose dispatching element doesn't sit inside an ancestor matching the selector:

{{-- Per-section toast surface: only handles events dispatched from inside the wrapper --}}
<div data-wk-section-toast>
    <x-wirekit::toast-region eventScope="[data-wk-section-toast]" position="top-right" />

    {{-- Buttons in this section dispatch the standard event; only THIS region picks it up --}}
    <x-wirekit::button @click="$dispatch('wirekit-toast', { variant: 'success', message: 'Saved' })">
        Save
    </x-wirekit::button>
</div>

name (event-name routing) and eventScope (DOM-containment filtering) are independent and may be combined — set both to require both a matching event name AND a dispatcher inside the scope wrapper. Default null means no containment filter (every dispatched event of the matching name is handled — the existing global-listener behavior).

This is the cleanest pattern for "per-card local toast queues" and similar layouts where the section that emits the toast is the section that should display it.

Pause on Hover

Auto-dismiss pauses when the user hovers over a toast and resumes when they move away. This is built-in — no configuration needed.

Dispatch Payload

The wirekit-toast event accepts:

Property Type Default Description
title string null Bold heading
message string '' Body text
variant string 'info' info | success | warning | danger
duration number Region default Auto-dismiss in ms (0 = persistent)

Width & Layout

Individual toasts have a fixed width of w-80 (20rem / 320px), capped to the viewport width minus 2rem on narrow screens (max-w-[calc(100vw-2rem)]). Position is controlled by the position prop on the toast region — not by CSS classes. Stack direction and spacing are handled automatically.

Props

Prop Type Default Description
position string 'top-right' Where toasts appear. One of: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right
duration number 5000 Default auto-dismiss duration in ms
max number 5 Maximum visible toasts (oldest removed when exceeded)
name string null Scoped name — listens on wirekit-toast-{name} instead of global wirekit-toast
label string null Accessible name for the region, and the switch that makes it a landmark (role="region"). Without it the region is not a landmark at all — set it only if every region on the page gets its own name
eventScope string null CSS selector — when set, only events whose dispatching element is inside an ancestor matching the selector are handled (DOM-containment filter, complements name's event-name routing)
filled bool false Use full variant background color instead of light tint (like a filled callout)
scope string null Scoped personalization key

Accessibility

  • The announcement is separate from the toast. The region renders two permanently-present visually-hidden live regions — one aria-live="polite", one aria-live="assertive", both aria-atomic="true" — and a new toast writes its text into one of them. A live region has to exist before the text it carries: an element that arrives already holding its message is a new node, not a region that changed, and assistive technology stays silent.
  • The toast card itself carries no live role and no aria-live. That is deliberate rather than missing. role="alert" implies aria-live="assertive", so putting it back on a card created inside the loop would either announce nothing (best case) or have the message read twice (worst case). The card is ordinary content, reachable and readable like any other.
  • danger goes to the assertive slot; everything else — including warning — goes to the polite one. Assertive interrupts whatever the reader is in the middle of, and a notice that dismisses itself after five seconds rarely earns that. A warning that genuinely must be acknowledged does not belong in a toast at all (see Pitfalls below); it belongs in <x-wirekit::alert-dialog> or an inline <x-wirekit::alert>, where nothing takes it away.
  • Each toast has a dismiss button labeled aria-label="Dismiss notification", translated through WireKit's own catalog.
  • Toasts never steal focus when they appear.
  • Decorative icons are aria-hidden="true".
  • Pass label to expose the whole region as a named landmark (role="region"). Without it the region is not a landmark — an unnamed one is worse than none, and several regions on a page sharing one built-in name is what landmark-unique reports.

Keyboard Interaction

The toast card is not focusable, but the dismiss button inside it is.

Key Action
Tab Move to the next dismiss button in the stack
Enter / Space Dismiss the focused toast

Two behaviors follow from that, and both are what a reader working the stack by keyboard needs:

  • Focus pauses the auto-dismiss timer, the same way hovering does — otherwise a toast disappears mid-reach.
  • Dismissing a toast hands focus to the next one (or to the previous one at the bottom of the stack, or back to wherever you tabbed in from once the stack is empty). Focus is never dropped to the top of the document.

Pitfalls

  • Don't use a toast for errors that block progress. Toasts auto-dismiss; errors that the user must acknowledge belong in <x-wirekit::alert-dialog> or an inline <x-wirekit::alert>.
  • Don't show more than 3 toasts simultaneously. Stacking degrades into noise — the component's queue dispatcher already enforces a soft limit, but custom dispatch logic should respect it.

Design Tokens

Toasts use the same tinted-background approach as Alert for visual consistency:

Token Usage
--color-wk-accent Info variant icon + border tint
--color-wk-success Success variant
--color-wk-warning Warning variant
--color-wk-danger Danger variant
--shadow-wk-lg Toast card elevation
--radius-wk-md Card corner radius
--padding-wk-x-md / --padding-wk-y-md Card padding
--font-wk-sans Font family
--transition-wk-duration Enter/leave animation timing

Customization

Override toast styles via personalization:

WireKit::personalize('toast-region', [
    'base' => 'fixed z-[9999] flex flex-col gap-4 p-6',
    'toast' => 'w-96 rounded-xl shadow-2xl',
]);

Was this page helpful?

Voting requires cookies or local storage. What we store