---
title: Toast
description: Auto-dismissing notification toast with variant + duration
visibility: guest
draft: false
---

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

```blade
{{-- 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.

:::preview{title="Trigger a toast"}
<div x-data>
<x-wirekit::toast-region name="docs-trigger-demo" position="top-right" />
<x-wirekit::button @click="$dispatch('wirekit-toast-docs-trigger-demo', {
    variant: 'success',
    title: 'Saved',
    message: 'Your changes were saved successfully.'
})">
    Save Changes
</x-wirekit::button>
</div>
:::

:::source{language="blade"}
{{-- Mounted once in your layout, not per page --}}
<x-wirekit::toast-region />

<x-wirekit::button wire:click="save">Save Changes</x-wirekit::button>
:::

:::source{language="php"}
// The toast reports what actually happened, so it is dispatched after the
// write returns — not from the click. A failed save takes the other branch
// and the user is told, instead of being congratulated by the markup.
public function save(): void
{
    try {
        $this->post->save();
    } catch (Throwable $e) {
        $this->dispatch('wirekit-toast',
            variant: 'danger',
            title: 'Could not save',
            message: $e->getMessage(),
        );

        return;
    }

    $this->dispatch('wirekit-toast',
        variant: 'success',
        title: 'Saved',
        message: 'Your changes were saved successfully.',
    );
}
:::

## Variants

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

:::preview{title="Toast variants"}
<div x-data>
<x-wirekit::toast-region name="docs-variants-demo" position="top-right" />

<x-wirekit::row wrap gap="sm">
<x-wirekit::button intent="neutral" surface="outline" @click="$dispatch('wirekit-toast-docs-variants-demo', {
    variant: 'info',
    title: 'Info',
    message: 'This is an informational notice.'
})">
    Show Info
</x-wirekit::button>

<x-wirekit::button @click="$dispatch('wirekit-toast-docs-variants-demo', {
    variant: 'success',
    title: 'Success',
    message: 'Record created successfully.'
})">
    Show Success
</x-wirekit::button>

<x-wirekit::button intent="neutral" surface="outline" @click="$dispatch('wirekit-toast-docs-variants-demo', {
    variant: 'warning',
    title: 'Warning',
    message: 'Your trial expires in 3 days.'
})">
    Show Warning
</x-wirekit::button>

<x-wirekit::button intent="danger" @click="$dispatch('wirekit-toast-docs-variants-demo', {
    variant: 'danger',
    title: 'Error',
    message: 'Failed to save changes.'
})">
    Show Danger
</x-wirekit::button>
</x-wirekit::row>
</div>
:::

:::source{language="blade"}
{{-- The four buttons above exist so you can see each variant on demand. In an
     application the variant is not a choice the markup makes — it follows from
     what happened, so one action dispatches whichever one fits. --}}
<x-wirekit::toast-region />

<x-wirekit::button wire:click="publish">Publish</x-wirekit::button>
:::

:::source{language="php"}
public function publish(): void
{
    if ($this->post->isEmpty()) {
        $this->dispatch('wirekit-toast', variant: 'warning',
            title: 'Nothing to publish', message: 'Add some content first.');

        return;
    }

    $this->post->publish();

    $this->dispatch('wirekit-toast', variant: 'success',
        title: 'Published', message: 'Your post is live.');
}
:::

## Position

Control where toasts appear on the screen:

```blade
<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:

:::preview{title="Top-left position"}
<div x-data>
<x-wirekit::toast-region name="docs-top-left" position="top-left" />
<x-wirekit::button @click="$dispatch('wirekit-toast-docs-top-left', {
    variant: 'info',
    title: 'Top-Left',
    message: 'Toast anchored to the top-left corner.'
})">
    Show Top-Left Toast
</x-wirekit::button>
</div>
:::

:::source{language="blade"}
{{-- The scoped name above belongs to this page, which mounts several regions
     at once. One region per layout needs none of it. --}}
<x-wirekit::toast-region position="top-left" />
:::

:::preview{title="Bottom-right position"}
<div x-data>
<x-wirekit::toast-region name="docs-bottom-right" position="bottom-right" />
<x-wirekit::button intent="neutral" surface="outline" @click="$dispatch('wirekit-toast-docs-bottom-right', {
    variant: 'success',
    title: 'Bottom-Right',
    message: 'Toast anchored to the bottom-right corner.'
})">
    Show Bottom-Right Toast
</x-wirekit::button>
</div>
:::

:::source{language="blade"}
<x-wirekit::toast-region position="bottom-right" />
:::

## Duration

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

```blade
{{-- All toasts in this region dismiss after 3 seconds --}}
<x-wirekit::toast-region :duration="3000" />
```

```javascript
// 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:

```blade
<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.

```blade
{{-- 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:

```javascript
// 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:

```blade
{{-- 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` |
| `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

- Info and success toasts use `role="status"` with `aria-live="polite"` — announced after the user finishes their current action.
- Warning and danger toasts use `role="alert"` with `aria-live="assertive"` — announced immediately.
- `aria-atomic="true"` ensures the full toast is read, not just the changed part.
- Each toast has a visible dismiss button with `aria-label="Dismiss notification"`.
- Toasts never steal focus from the page — they are purely informational.
- Decorative icons are `aria-hidden="true"`.

## Keyboard Interaction

This component is purely presentational and does not respond to keyboard input.

## 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](/components/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](/customization):

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