ToastStack
ToastStack is the region a page mounts once to render whatever the toast queue holds. It owns placement, stacking order and dismissal; it does not decide when a toast appears — that is the store's job, and any code with access to the store can push one.
React has no component with this name: there, the same region is rendered by ToastProvider. The
split exists because Alpine has no provider shape — a store plus a mount point does the same work
with fewer moving parts.
When to use
Mount exactly one ToastStack per document, near the end of <body>, and leave it there. Everything
else talks to the store.
Reach for something else when:
- The message belongs to a specific field or form — use Alert, which stays in the flow next to what it describes and does not disappear on a timer.
- The message needs an answer before anything else happens — use Dialog, which blocks.
- The page has nothing to announce yet — an empty stack renders nothing, so there is no state to design for.
Accessibility
- The stack itself is not live. Two persistent regions inside it exist before any toast: polite for info and success, assertive for danger. Errors interrupt; everything else waits its turn.
- Rows carry no
roleof their own — arole="status"node inserted together with its text is often not spoken. - Dismissal is reachable by keyboard on every toast — a timer is a convenience, never the only way out.
- Toasts stack in queue order, so the reading order matches the visual order.
API and code
React: React renders the queue through ToastProvider instead of a standalone component.
x-data="lyraToastStack({ … })"
This binding takes no options — mount it and it works.
The stack takes no options — it renders whatever the store holds. Mount one per document and
push toasts from anywhere with $store.lyraToasts:
<div class="lyra-toast-stack" x-data="lyraToastStack()">
<div
data-lyra-toast-region="polite"
aria-live="polite"
aria-relevant="additions"
style="display: contents"
>
<template x-for="toast in politeToasts" :key="toast.id">
<div class="lyra-toast">
<span class="lyra-toast__icon" :class="toneClass(toast.tone)"></span>
<span x-text="toast.message"></span>
<button class="lyra-toast__close" :data-toast-id="toast.id" x-bind="closeButton">×</button>
</div>
</template>
</div>
<div
data-lyra-toast-region="assertive"
aria-live="assertive"
aria-relevant="additions"
style="display: contents"
>
<template x-for="toast in assertiveToasts" :key="toast.id">
<div class="lyra-toast">
<span class="lyra-toast__icon" :class="toneClass(toast.tone)"></span>
<span x-text="toast.message"></span>
<button class="lyra-toast__close" :data-toast-id="toast.id" x-bind="closeButton">×</button>
</div>
</template>
</div>
</div>Each toast carries its own id, so the close button reads it from the element instead of a closure.
toneClass() maps the tone to its icon modifier; the icon itself is yours to provide.
<lyra:toast-stack> Generated from lyra-ds/blade v0.10.0.
The behavior comes from lyraToastStack() — install @lyra-ds/alpine and see the HTML + Alpine tab.
This component takes no props — drop the tag in.
<lyra:toast-stack />