Skip to content
Docs

Toast

A succinct message that is displayed temporarily.

A succinct message that is displayed temporarily.

<Toast.provider duration={8000} swipe_direction="right" label="Notification">
  <button
    type="button"
    id="docs-toast-show-btn"
    class="DemoButton large violet"
    onclick="const toast = document.getElementById('docs-toast-primitive'); if (!toast) return; toast.hidden = false; toast.dataset.state = 'open'; toast.dispatchEvent(new CustomEvent('essence:toast:open', {bubbles: true}));"
  >
    Add to calendar
  </button>
  <Toast.viewport id="docs-toast-viewport" class="DemoToastViewport">
    <Toast.root
      id="docs-toast-primitive"
      duration={3000}
      type="foreground"
      default_open={false}
      class="DemoToastRoot"
    >
      <Toast.title class="DemoToastTitle">Scheduled: Catch up</Toast.title>
      <Toast.description class="DemoToastDescription">Friday at 5:30 PM</Toast.description>
      <Toast.action
        alt_text="Goto schedule to undo"
        class="DemoToastAction DemoButton small green"
      >
        Undo
      </Toast.action>
      <Toast.close class="DemoToastClose" aria-label="Close">×</Toast.close>
    </Toast.root>
  </Toast.viewport>
</Toast.provider>
Automatically closes.
Pauses closing on hover, focus and window blur.
Supports hotkey to jump to toast viewport.
Supports closing via swipe gesture.
Exposes CSS variables for swipe gesture animations.
Can be controlled or uncontrolled.

Anatomy

Import the component.

<Toast.provider>
  <Toast.root>
    <Toast.title />
    <Toast.description />
    <Toast.action />
    <Toast.close />
  </Toast.root>
  <Toast.viewport />
</Toast.provider>

Anatomy

Provider
Wraps toasts and the viewport. Supplies default duration, swipe direction, and label.
Viewport
The fixed area where toasts appear.
Root
An individual toast that auto-dismisses.
Title
An optional title for the toast.
Description
The toast message.
Action
An optional action button. Requires `alt_text` for screen readers.
Close
A button that dismisses the toast before its duration elapses.

API Reference

Provider

The provider that wraps your toasts and toast viewport. It usually wraps the application.

Prop Type Default Description
duration integer 5000
label string "Notification"
swipe_direction string "right"
duration
Type integer Default 5000
label
Type string Default "Notification"
swipe_direction
Type string Default "right"

Viewport

The fixed area where toasts appear. Users can jump to the viewport by pressing the configured hotkey (F8 by default). It is up to you to ensure the discoverability of the hotkey for keyboard users.

Prop Type Default Description
hotkey string "F8"
id string nil
label string "Notifications ({hotkey})"
hotkey
Type string Default "F8"
id
Type string Default nil
label
Type string Default "Notifications ({hotkey})"

Root

The toast that automatically closes. It should not be held open to acquire a user response—use an AlertDialog styled as a toast when a response is required.

Use open with on_open_change for controlled state in LiveView:

<Toast.root id="saved-toast" open={@toast_open} on_open_change="toast_open_change">
  …
</Toast.root>
def handle_event("toast_open_change", %{"open" => open}, socket) do
  {:noreply, assign(socket, toast_open: open)}
end

The ToastRoot hook pauses the dismiss timer on pointer enter and focus, resumes on leave/blur, closes on Escape when focused, and dispatches essence:toast:open / essence:toast:close custom events for imperative control.

Prop Type Default Description
default_open boolean true
duration integer nil
id string
on_open_change string nil
open boolean nil
type string "foreground"
default_open
Type boolean Default true
duration
Type integer Default nil
id
Type string Default
on_open_change
Type string Default nil
open
Type boolean Default nil
type
Type string Default "foreground"

Data attributes

Attribute Values Description
[data-state] open | closed Reflects the open state.
[data-swipe] start | move | cancel | end Present during swipe interactions (CSS-ready; pointer swipe handling is not yet implemented in the hook).
[data-swipe-direction] up | down | left | right Synced from the provider for styling swipe animations.

CSS variables for swipe animations (set when swipe handling is active):

| CSS variable | Description | | --- | --- | | --radix-toast-swipe-move-x | Horizontal offset while swiping | | --radix-toast-swipe-move-y | Vertical offset while swiping | | --radix-toast-swipe-end-x | Final horizontal offset after swipe | | --radix-toast-swipe-end-y | Final vertical offset after swipe |

Title

An optional title for the toast.

Prop Type Default Description

Description

The toast message.

Prop Type Default Description

Action

An action that is safe to ignore so users are not expected to complete tasks with unexpected side effects as a result of a time limit.

When obtaining a user response is necessary, portal an AlertDialog styled as a toast into the viewport instead.

Prop Type Default Description
alt_text string
alt_text
Type string Default

Close

A button that allows users to dismiss the toast before its duration has elapsed.

Prop Type Default Description

Examples

Custom hotkey

Override the default hotkey on the viewport. Essence accepts a single key name (for example "F8" or "T"); modifier combinations are not yet supported.

<Toast.provider>
  …
  <Toast.viewport hotkey="T" label="Notifications (T)" />
</Toast.provider>

Press the configured key to focus the viewport region.

Custom duration

Customise the duration of a toast to override the provider value.

<Toast.root id="saved" duration={3000}>
  <Toast.description>Saved!</Toast.description>
</Toast.root>

Duplicate toasts

When a toast must appear every time a user clicks a button, use LiveView state to render multiple instances of the same toast:

<Toast.provider>
  <form phx-submit="save">
    <button type="submit">Save</button>
  </form>
  <Toast.viewport>
    <%= for id <- @toast_ids do %>
      <Toast.root id={id} default_open={true}>
        <Toast.description>Saved!</Toast.description>
      </Toast.root>
    <% end %>
  </Toast.viewport>
</Toast.provider>
def handle_event("save", _params, socket) do
  id = "toast-#{System.unique_integer([:positive])}"
  {:noreply, update(socket, :toast_ids, &[id | &1])}
end

Animating swipe gesture

Combine --radix-toast-swipe-move-[x|y] and --radix-toast-swipe-end-[x|y] CSS variables with data-swipe="[start|move|cancel|end]" attributes to animate a swipe-to-close gesture. The stylesheet below matches the demo CSS; the ToastRoot hook currently syncs data-swipe-direction from the provider—pointer swipe events that set data-swipe are not yet implemented.

<Toast.provider swipe_direction="right">
  <Toast.root id="swipe-toast" class="DemoToastRoot">…</Toast.root>
  <Toast.viewport />
</Toast.provider>
.DemoToastRoot[data-swipe="move"] {
  transform: translateX(var(--radix-toast-swipe-move-x));
}
.DemoToastRoot[data-swipe="cancel"] {
  transform: translateX(0);
  transition: transform 200ms ease-out;
}
.DemoToastRoot[data-swipe="end"] {
  animation: slideRight 100ms ease-out;
}

@keyframes slideRight {
  from {
    transform: translateX(var(--radix-toast-swipe-end-x));
  }
  to {
    transform: translateX(100%);
  }
}

Accessibility

Adheres to the aria-live requirements.

Sensitivity

Control the sensitivity of the toast for screen readers using the type attribute.

For toasts that are the result of a user action, choose foreground. Toasts generated from background tasks should use background.

Foreground

Foreground toasts are announced immediately. Assistive technologies may choose to clear previously queued messages when a foreground toast appears. Try to avoid stacking distinct foreground toasts at the same time.

Background

Background toasts are announced at the next graceful opportunity, for example when the screen reader has finished reading its current sentence. They do not clear queued messages so overusing them can be perceived as a laggy user experience for screen reader users when used in response to a user interaction.

<Toast.root id="foreground-toast" type="foreground">
  <Toast.description>File removed successfully.</Toast.description>
  <Toast.close aria-label="Close">Dismiss</Toast.close>
</Toast.root>

<Toast.root id="background-toast" type="background">
  <Toast.description>We've just released Essence UI 1.0.</Toast.description>
  <Toast.close aria-label="Close">Dismiss</Toast.close>
</Toast.root>

Alternative action

Use the alt_text attribute on Toast.action to instruct an alternative way of actioning the toast to screen reader users.

<Toast.root id="upgrade-toast" type="background">
  <Toast.title>Upgrade Available!</Toast.title>
  <Toast.description>We've just released Essence UI 1.0.</Toast.description>
  <Toast.action alt_text="Goto account settings to upgrade">Upgrade</Toast.action>
  <Toast.close aria-label="Close">Dismiss</Toast.close>
</Toast.root>

Close icon button

When providing an icon (or font icon), remember to label it correctly for screen reader users.

<Toast.root id="saved-icon-toast" type="foreground">
  <Toast.description>Saved!</Toast.description>
  <Toast.close aria-label="Close">
    <span aria-hidden="true">×</span>
  </Toast.close>
</Toast.root>

Keyboard Interactions

Keyboard Interactions

Key Description
F8 Focuses the toast viewport (or the key configured via `hotkey`).
Tab Moves focus to the next focusable element.
Shift + Tab Moves focus to the previous focusable element.
Space When focus is on a `Toast.action` or `Toast.close`, activates the control.
Enter When focus is on a `Toast.action` or `Toast.close`, activates the control.
Escape When focus is on a toast, closes the toast.

Custom APIs

Abstract parts

Create your own API by abstracting the primitive parts into your own component.

Usage

<.toast id="upgrade" title="Upgrade available" content="We've just released Essence UI 1.0!">
  Upgrade
</.toast>

Implementation

def toast(assigns) do
  ~H"""
  <Toast.root id={@id} {@rest}>
    <Toast.title :if={@title}>{@title}</Toast.title>
    <Toast.description>{@content}</Toast.description>
    <Toast.action :if={@inner_block != []} alt_text={@alt_text || "Perform action"}>
      {render_slot(@inner_block)}
    </Toast.action>
    <Toast.close aria-label="Close">
      <span aria-hidden="true">×</span>
    </Toast.close>
  </Toast.root>
  """
end

Imperative API

Create your own imperative API to allow toast duplication if preferred.

Usage

Track toast count in LiveView and expose a phx-click that increments it:

<Toast.provider>
  <button type="button" phx-click="show_saved_toast">Save</button>
  <Toast.viewport>
    <%= for id <- @saved_toast_ids do %>
      <Toast.root id={id} default_open={true}>
        <Toast.description>Saved successfully!</Toast.description>
        <Toast.close aria-label="Close">Dismiss</Toast.close>
      </Toast.root>
    <% end %>
  </Toast.viewport>
</Toast.provider>

Implementation

def handle_event("show_saved_toast", _params, socket) do
  id = "saved-#{System.unique_integer([:positive])}"
  {:noreply, assign(socket, saved_toast_ids: [id | socket.assigns.saved_toast_ids])}
end