Corex. Toast
(Corex v0.2.0)
View Source
Toast notifications for Phoenix LiveView. Behavior follows Zag.js Toast.
Replace layout flash groups with <.toast_group> and drive toasts from LiveView or the client API.
Anatomy
Layout flash
<.toast_group id="layout-toast" flash={@flash} class="toast">
<:loading>
<.heroicon name="hero-arrow-path" />
</:loading>
</.toast_group>Flash defaults
<.toast_group
id="layout-toast"
class="toast"
flash={@flash}
flash_info={%{title: "Success", type: :success, duration: 5000}}
flash_error={%{title: "Error", type: :error, duration: :infinity}}
/>API
Target a toast group by its DOM id on <.toast_group>.
| Function | Action | Returns |
|---|---|---|
create/5 | Create toast (client) | %Phoenix.LiveView.JS{} |
create/6 | Create toast (server) | socket |
update/3 | Update toast (client) | %Phoenix.LiveView.JS{} |
update/4 | Update toast (server) | socket |
remove/2 | Remove toast immediately (client) | %Phoenix.LiveView.JS{} |
remove/3 | Remove toast immediately (server) | socket |
dismiss/2 | Dismiss with lifecycle (client) | %Phoenix.LiveView.JS{} |
dismiss/3 | Dismiss with lifecycle (server) | socket |
create (client)
<.action
phx-click={Corex.Toast.create("layout-toast", "Info", "Info description", :info, [])}
class="button ui-size-sm"
>
Info
</.action>create (server)
def handle_event("create_info_toast", _, socket) do
{:noreply,
Corex.Toast.create(
socket,
"layout-toast",
"Info",
"Info description",
:info,
duration: 5000
)}
endcreate opts: duration, loading: true, id: "stable-id", priority: 1–8, action: map with label (string or ~H / safe HTML), js, optional class.
:action is server-only. Pass it on create/6 / update/4 (push_event). Client
bindings (create/5, update/3) and DOM CustomEvents ignore :action so untrusted
scripts cannot inject exec_js or HTML labels.
HTML action labels (~H / {:safe, _}) are rendered with innerHTML in the client.
Pass developer-controlled markup only — never pipe untrusted user input into
safe HTML labels.
Style
Target parts with data-scope and data-part, or use Corex Design: import tokens and toast.css, then set class="toast" on <.toast_group>.
[data-scope="toast"][data-part="group"] {}
[data-scope="toast"][data-part="root"] {}
[data-scope="toast"][data-part="title"] {}
[data-scope="toast"][data-part="description"] {}
[data-scope="toast"][data-part="close-trigger"] {}
[data-scope="toast"][data-part="action-trigger"] {}@import "../corex/corex.css";Stack modifiers on the group host (class on <.toast_group>).
Axes: Semantic (ui-accent, ui-brand, ui-alert, ui-info, ui-success), Size (ui-size-sm … ui-size-xl), Radius (ui-rounded-*). No variant axis. See the modifier guide.
Semantic modifiers set palette variables on action triggers and content panels.
Semantic
| Modifier | Classes |
|---|---|
| Default | toast |
| Accent | toast ui-accent |
| Brand | toast ui-brand |
| Alert | toast ui-alert |
| Info | toast ui-info |
| Success | toast ui-success |
Summary
Components
Renders a div that creates a toast notification when a client error occurs.
Renders a div that creates a toast notification when the connection is restored.
Renders a div that creates a toast notification when the connection is lost.
Renders a toast group (toaster) that manages multiple toast notifications.
Renders a div that creates a toast notification when a server error occurs.
API
Append a toast from phx-click. Dispatches corex:toast:create on the toast group host (id). Optional keyword opts: :id, :duration, :loading, :priority. Prefer create/6 when you need :action.
Append a toast from handle_event (toast_create). Payload includes group_id plus Zag fields assembled from the same arguments. Use this path for :action (client create/5 ignores it).
Begin dismiss animation from phx-click. Dispatches corex:toast:dismiss with detail.id.
Begin dismiss animation from handle_event (toast_dismiss).
Remove a toast immediately from phx-click. Dispatches corex:toast:remove with detail.id.
Remove a toast immediately from handle_event (toast_remove).
Patch an existing toast from phx-click. Dispatches corex:toast:update with detail.id and optional fields from attrs (map or keyword list).
Patch an existing toast from handle_event (toast_update).
Components
Renders a div that creates a toast notification when a client error occurs.
This component should be placed in your layout and will automatically create a toast when Phoenix LiveView detects a client-side connection error.
Anatomy
<.toast_client_error
toast_group_id="layout-toast"
title="We can't find the internet"
description="Attempting to reconnect"
type={:loading}
duration={:infinity}
/>Attributes
id(:string) - Defaults to"client-error-toast".toast_group_id(:string) (required)title(:string) (required)description(:string) - Defaults tonil.type(:atom) - Defaults to:info. Must be one of:info,:success, or:error.duration(:any) - Defaults to:infinity.
Renders a div that creates a toast notification when the connection is restored.
This component should be placed in your layout and will automatically create a toast when Phoenix LiveView detects that the connection has been restored.
Anatomy
<.toast_connected
toast_group_id="layout-toast"
title="Connection restored"
description="You're back online"
type={:success}
/>Attributes
id(:string) - Defaults to"connected-toast".toast_group_id(:string) (required)title(:string) (required)description(:string) - Defaults tonil.type(:atom) - Defaults to:success. Must be one of:info,:success, or:error.duration(:any) - Defaults to5000.
Renders a div that creates a toast notification when the connection is lost.
This component should be placed in your layout and will automatically create a toast when Phoenix LiveView detects that the connection has been lost.
Anatomy
<.toast_disconnected
toast_group_id="layout-toast"
title="Connection lost"
description="Attempting to reconnect"
type={:info}
duration={:infinity}
/>Attributes
id(:string) - Defaults to"disconnected-toast".toast_group_id(:string) (required)title(:string) (required)description(:string) - Defaults tonil.type(:atom) - Defaults to:info. Must be one of:info,:success, or:error.duration(:any) - Defaults to:infinity.
Renders a toast group (toaster) that manages multiple toast notifications.
This component should be rendered once in your layout.
Anatomy
<.toast_group id="layout-toast" class="toast" flash={@flash}>
<:loading>
<.heroicon name="hero-arrow-path" />
</:loading>
</.toast_group>Flash Messages
You can use the flash attribute to display flash messages as toasts.
Optional flash_info and flash_error accept maps with atom keys title, type, and duration for defaults when rendering info and error flashes.
The description comes from the Phoenix flash message.
<.toast_group
id="layout-toast"
class="toast"
flash={@flash}
flash_info={%{title: "Success", type: :success, duration: 5000}}
flash_error={%{title: "Error", type: :error, duration: :infinity}}/>Attributes
id(:string) (required) - The id of the toast group.placement(:string) - Where toasts appear on screen. Defaults to"bottom-end". Must be one of"top-start","top","top-end","bottom-start","bottom", or"bottom-end".overlap(:boolean) - Whether toasts can overlap. Defaults totrue.max(:integer) - Maximum number of visible toasts. Defaults to5.gap(:integer) - Gap between toasts in pixels. Defaults tonil.offset(:string) - Offset from viewport edge. Defaults tonil.pause_on_page_idle(:boolean) - Pause duration when page is idle. Defaults tofalse.flash(:map) - The map of flash messages to display as toasts. Defaults to%{}.flash_info(:any) - Defaults for:infoflashes:Corex.Flash.Info, map, or omit for translation defaults. Defaults tonil.flash_error(:any) - Defaults for:errorflashes:Corex.Flash.Error, map, or omit for translation defaults. Defaults tonil.translation(Corex.Toast.Translation) - Override default titles for info/error flash messages. Defaults tonil.- Global attributes are accepted.
Slots
loading- the loading spinner icon to display when duration is infinity. Accepts attributes:class(:string)
close- content placed in each toast close button (hidden template, cloned in the client).
Renders a div that creates a toast notification when a server error occurs.
This component should be placed in your layout and will automatically create a toast when Phoenix LiveView detects a server-side connection error.
Anatomy
<.toast_server_error
toast_group_id="layout-toast"
title="Something went wrong!"
description="Attempting to reconnect"
type={:error}
duration={:infinity}
/>Attributes
id(:string) - Defaults to"server-error-toast".toast_group_id(:string) (required)title(:string) (required)description(:string) - Defaults tonil.type(:atom) - Defaults to:error. Must be one of:info,:success, or:error.duration(:any) - Defaults to:infinity.
API
@spec create( String.t(), Corex.Value.coercible(), Corex.Value.coercible(), Corex.Value.coercible(), keyword() ) :: Phoenix.LiveView.JS.t()
Append a toast from phx-click. Dispatches corex:toast:create on the toast group host (id). Optional keyword opts: :id, :duration, :loading, :priority. Prefer create/6 when you need :action.
<.action phx-click={Corex.Toast.create("toast-group-id", "Saved", "Draft stored.", :info, duration: 4_000)}>Notify</.action>
<.toast_group id="toast-group-id" placement="bottom-end" />document.getElementById("toast-group-id")?.dispatchEvent(
new CustomEvent("corex:toast:create", {
bubbles: true,
detail: { group_id: "toast-group-id", title: "Saved", description: "OK.", type: "info", duration: 4000 },
})
);
@spec create( Phoenix.LiveView.Socket.t(), String.t(), Corex.Value.coercible(), Corex.Value.coercible(), Corex.Value.coercible(), keyword() ) :: Phoenix.LiveView.Socket.t()
Append a toast from handle_event (toast_create). Payload includes group_id plus Zag fields assembled from the same arguments. Use this path for :action (client create/5 ignores it).
def handle_event("notify", _, socket) do
{:noreply,
Corex.Toast.create(socket, "toast-group-id", "Done", "Completed.", :success,
duration: 3_000,
action: %{label: "Undo", js: JS.push("undo"), class: "button ui-size-sm"}
)}
end
@spec dismiss(String.t(), String.t()) :: Phoenix.LiveView.JS.t()
Begin dismiss animation from phx-click. Dispatches corex:toast:dismiss with detail.id.
<.action phx-click={Corex.Toast.dismiss("toast-group-id", @toast_id)}>Dismiss</.action>
<.toast_group id="toast-group-id" />
@spec dismiss(Phoenix.LiveView.Socket.t(), String.t(), String.t()) :: Phoenix.LiveView.Socket.t()
Begin dismiss animation from handle_event (toast_dismiss).
def handle_event("fade_out", %{"id" => id}, socket) do
{:noreply, Corex.Toast.dismiss(socket, "toast-group-id", id)}
end
@spec remove(String.t(), String.t()) :: Phoenix.LiveView.JS.t()
Remove a toast immediately from phx-click. Dispatches corex:toast:remove with detail.id.
<.action phx-click={Corex.Toast.remove("toast-group-id", @toast_id)}>Remove</.action>
<.toast_group id="toast-group-id" />
@spec remove(Phoenix.LiveView.Socket.t(), String.t(), String.t()) :: Phoenix.LiveView.Socket.t()
Remove a toast immediately from handle_event (toast_remove).
def handle_event("drop_toast", %{"id" => id}, socket) do
{:noreply, Corex.Toast.remove(socket, "toast-group-id", id)}
end
@spec update(String.t(), String.t(), keyword()) :: Phoenix.LiveView.JS.t()
Patch an existing toast from phx-click. Dispatches corex:toast:update with detail.id and optional fields from attrs (map or keyword list).
<.action phx-click={Corex.Toast.update("toast-group-id", @toast_id, title: "Uploading…", loading: true)}>Update</.action>
<.toast_group id="toast-group-id" />
@spec update(Phoenix.LiveView.Socket.t(), String.t(), String.t(), keyword()) :: Phoenix.LiveView.Socket.t()
Patch an existing toast from handle_event (toast_update).
def handle_event("patch_toast", %{"id" => id}, socket) do
{:noreply, Corex.Toast.update(socket, "toast-group-id", id, %{title: "Finished", loading: false})}
end