Fluxon (Fluxon v3.1.1)

To use Fluxon components in your LiveView or View modules, add the following to your module:

use Fluxon

This will import all available components. You can customize which components to import using the following options:

  • :only - List of components to import. Only the specified components will be imported.

    use Fluxon, only: [:button, :input, :modal]
  • :except - List of components to exclude. All components except the specified ones will be imported.

    use Fluxon, except: [:table, :tabs]

Summary

Functions

Removes any flash-queued toasts that have not yet been delivered to the client.

Closes a autocomplete component by id. Same call shapes as open_autocomplete/1.

Closes a date picker component by id. Same call shapes as open_date_picker/1.

Closes a dialog component by id. Same call shapes as open_dialog/1.

Closes a dropdown component by id. Same call shapes as open_dropdown/1.

Closes a popover component by id. Same call shapes as open_popover/1.

Closes a select component by id. Same call shapes as open_select/1.

Closes a tags input component by id. Same call shapes as open_tags_input/1.

Closes a tooltip component by id. Same call shapes as open_tooltip/1.

Programmatically dismisses a toast by id.

Opens a autocomplete component by id.

Opens a autocomplete component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a date picker component by id.

Opens a date picker component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a dialog component by id.

Opens a dialog component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a dropdown component by id.

Opens a dropdown component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a popover component by id.

Opens a popover component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a select component by id.

Opens a select component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a tags input component by id.

Opens a tags input component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Opens a tooltip component by id.

Opens a tooltip component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Queues a toast notification that survives navigation.

Resolves a previously-shown toast (typically a loading toast) with a new title and, optionally, a new color.

Shows a rich, in-LiveView toast.

Shows an indeterminate loading toast, designed to be resolved later via resolve_toast/4.

Toggles a autocomplete component by id. Same call shapes as open_autocomplete/1.

Toggles a date picker component by id. Same call shapes as open_date_picker/1.

Toggles a dialog component by id. Same call shapes as open_dialog/1.

Toggles a dropdown component by id. Same call shapes as open_dropdown/1.

Toggles a popover component by id. Same call shapes as open_popover/1.

Toggles a select component by id. Same call shapes as open_select/1.

Toggles a tags input component by id. Same call shapes as open_tags_input/1.

Toggles a tooltip component by id. Same call shapes as open_tooltip/1.

Functions

clear_toasts(socket)

Removes any flash-queued toasts that have not yet been delivered to the client.

Useful in long-lived LiveViews where toasts have accumulated under the Fluxon flash key but no navigation has flushed them. After this call, pending flash toasts are wiped - already-visible toasts are unaffected; use dismiss_toast/2 for those.

Accepts both Phoenix.LiveView.Socket and Plug.Conn.

close_autocomplete(id)

Closes a autocomplete component by id. Same call shapes as open_autocomplete/1.

close_autocomplete(socket, id)

close_date_picker(id)

Closes a date picker component by id. Same call shapes as open_date_picker/1.

close_date_picker(socket, id)

close_dialog(id)

Closes a dialog component by id. Same call shapes as open_dialog/1.

close_dialog(socket, id)

close_dropdown(id)

Closes a dropdown component by id. Same call shapes as open_dropdown/1.

close_dropdown(socket, id)

close_popover(id)

Closes a popover component by id. Same call shapes as open_popover/1.

close_popover(socket, id)

close_select(id)

Closes a select component by id. Same call shapes as open_select/1.

close_select(socket, id)

close_tags_input(id)

Closes a tags input component by id. Same call shapes as open_tags_input/1.

close_tags_input(socket, id)

close_tooltip(id)

Closes a tooltip component by id. Same call shapes as open_tooltip/1.

close_tooltip(socket, id)

dismiss_toast(toast_id)

Programmatically dismisses a toast by id.

Has three forms, mirroring send_toast/3:

  • dismiss_toast(socket, id) - pushes a channel event from a handler.
  • dismiss_toast(id) - returns a %JS{} for use in phx-click={...}.
  • dismiss_toast(%JS{}, id) - chains onto an existing JS pipeline.

Works for both flash-queued (put_toast/3) and live (send_toast/3) toasts - once shown, every toast is identified the same way on the client. Calling this for an unknown id is a no-op.

Examples

# From a server handler
def handle_info({:upload_done, id}, socket) do
  {:noreply, Fluxon.dismiss_toast(socket, id)}
end

# From a phx-click, no server round-trip
<.button phx-click={Fluxon.dismiss_toast("upload-progress")}>
  Cancel
</.button>

dismiss_toast(socket, toast_id)

open_autocomplete(id)

Opens a autocomplete component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_autocomplete("my-autocomplete")}>Open</.button>
<.autocomplete id="my-autocomplete"></.autocomplete>

open_autocomplete(socket, id)

Opens a autocomplete component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_autocomplete(socket, "my-autocomplete")}
end

JS.push("logged") |> Fluxon.open_autocomplete("my-autocomplete")

open_date_picker(id)

Opens a date picker component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_date_picker("my-date_picker")}>Open</.button>
<.date_picker id="my-date_picker"></.date_picker>

open_date_picker(socket, id)

Opens a date picker component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_date_picker(socket, "my-date_picker")}
end

JS.push("logged") |> Fluxon.open_date_picker("my-date_picker")

open_dialog(id)

Opens a dialog component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_dialog("my-dialog")}>Open</.button>
<.modal id="my-dialog"></.modal>

open_dialog(socket, id)

Opens a dialog component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_dialog(socket, "my-dialog")}
end

JS.push("logged") |> Fluxon.open_dialog("my-dialog")

open_dropdown(id)

Opens a dropdown component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_dropdown("my-dropdown")}>Open</.button>
<.dropdown id="my-dropdown"></.dropdown>

open_dropdown(socket, id)

Opens a dropdown component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_dropdown(socket, "my-dropdown")}
end

JS.push("logged") |> Fluxon.open_dropdown("my-dropdown")

open_popover(id)

Opens a popover component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_popover("my-popover")}>Open</.button>
<.popover id="my-popover"></.popover>

open_popover(socket, id)

Opens a popover component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_popover(socket, "my-popover")}
end

JS.push("logged") |> Fluxon.open_popover("my-popover")

open_select(id)

Opens a select component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_select("my-select")}>Open</.button>
<.select id="my-select"></.select>

open_select(socket, id)

Opens a select component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_select(socket, "my-select")}
end

JS.push("logged") |> Fluxon.open_select("my-select")

open_tags_input(id)

Opens a tags input component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_tags_input("my-tags_input")}>Open</.button>
<.tags_input id="my-tags_input"></.tags_input>

open_tags_input(socket, id)

Opens a tags input component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_tags_input(socket, "my-tags_input")}
end

JS.push("logged") |> Fluxon.open_tags_input("my-tags_input")

open_tooltip(id)

Opens a tooltip component by id.

Returns a Phoenix.LiveView.JS struct that dispatches a DOM event to the target element. The id may be passed as "my-id" or "#my-id"; both forms resolve to the same target.

Examples

<.button phx-click={Fluxon.open_tooltip("my-tooltip")}>Open</.button>
<.tooltip id="my-tooltip"></.tooltip>

open_tooltip(socket, id)

Opens a tooltip component from a server handler (Socket) or by chaining onto an existing Phoenix.LiveView.JS pipeline.

Examples

def handle_event("show", _, socket) do
  {:noreply, Fluxon.open_tooltip(socket, "my-tooltip")}
end

JS.push("logged") |> Fluxon.open_tooltip("my-tooltip")

put_toast(conn_or_socket, title, opts \\ [])

Queues a toast notification that survives navigation.

Works with both Plug.Conn (controllers, dead views) and Phoenix.LiveView.Socket. Toast data rides on Phoenix's flash mechanism, so it survives redirect/2, push_navigate/2, live_redirect, and full-page reloads - the same call works everywhere.

Because toast data must serialize through the flash transport, put_toast/3 only accepts plain values. For richer toasts (HEEx content, callback actions, loading/resolve), use send_toast/3 instead - those are in-LiveView only.

Options

  • :color - one of :info, :success, :warning, :danger, or nil for the neutral default. Error toasts are announced assertively for screen readers.
  • :description - secondary text rendered below the title.
  • :dismiss_after - auto-dismiss in milliseconds. Defaults to the toaster's dismiss_after (typically 4000). Pass nil for persistent toasts that only go away when the user dismisses them or you call dismiss_toast/2.
  • :dismissible - whether the user can dismiss the toast (X button, swipe, Escape). Defaults to true.
  • :icon - pass false to hide the color icon. Custom HEEx icons are not supported via this transport - use send_toast/3.
  • :action / :actions - one map or a list of maps. Each action map requires :label and :type. Allowed types here are :link (with exactly one of :href, :navigate, or :patch - mirroring Phoenix's <.link> component) and :dismiss (no other side effect). Optional :position (:inline or :below, default :below), :variant (outline, solid, soft, surface, ghost, link), :disabled, and :dismiss (default true; pass false to keep the toast open after the action fires). Callback actions (type: :event) are not allowed here - use send_toast/3.
  • :id - custom toast id. An opaque id is generated when omitted. Provide one explicitly when you intend to reference the toast later with dismiss_toast/2 or resolve_toast/4.

Setup

Pass @flash to the toaster in your root layout:

<.toaster flash={@flash} />

Examples

# Controller  -  redirect shows the toast on the destination page
conn
|> Fluxon.put_toast("Welcome back!", color: :info)
|> redirect(to: ~p"/")

# Controller  -  render shows the toast on the rendered page itself
conn
|> Fluxon.put_toast("Could not save", color: :danger)
|> render(:edit, changeset: changeset)

# LiveView handler  -  survives push_navigate
{:noreply,
 socket
 |> Fluxon.put_toast("Saved", color: :success)
 |> push_navigate(to: ~p"/dashboard")}

# Link action
Fluxon.put_toast(socket, "Account created",
  action: %{label: "View profile", type: :link, href: ~p"/profile"})

Notes and limits

  • Redirect when the toast must survive navigation. Phoenix only persists flash to the session cookie when the response status is in 300..308. With a 200 render the toast still shows - the rendered response carries the payload and the standalone runtime surfaces it - but it does not survive a further navigation.
  • Flash list is capped. Phoenix flash rides on the session cookie (~4KB hard limit), so the queued list is capped both by entry count and by encoded byte size. Excess toasts are silently dropped (oldest first) to keep the cookie within budget.
  • Signed-token TTL is 60 seconds. For push_navigate / live_redirect, Phoenix signs the flash with a 60-second TTL. Long-running async work between put_toast and the navigation can push the token past expiry.
  • Unsafe :href schemes are rejected. Only http, https, mailto, tel, sms, and paths starting with /, #, ?, or // are allowed. javascript:, data:, vbscript:, and friends raise ArgumentError. :navigate and :patch accept only relative paths starting with /, #, or ?.

Returns

  • For Phoenix.LiveView.Socket - an updated socket. Chain with push_navigate/2 / redirect/2 or return as-is from a handler.
  • For Plug.Conn - an updated conn. Chain with redirect/2.

resolve_toast(toast_id, title)

Resolves a previously-shown toast (typically a loading toast) with a new title and, optionally, a new color.

Has three forms, mirroring send_toast/3:

  • resolve_toast(socket, toast_id, title, opts) - pushes a channel event from a handle_event / handle_info. Returns the socket.
  • resolve_toast(toast_id, title, opts) - returns a Phoenix.LiveView.JS command for use in phx-click={...}, resolving the toast with no server round-trip.
  • resolve_toast(%JS{}, toast_id, title, opts) - chains onto an existing JS pipeline.

The :color lives in opts like every other toast helper. Omit it to keep the toast's current color (e.g. updating the title of a still-loading toast); pass one to flip it (e.g. color: :success). Accepts the same options as send_toast/3 for the resolved state - including :description, :dismiss_after, :action, :dismissible.

Re-uses the toast's existing DOM node when possible; swaps in a new node when the color changes, so the resolution animates smoothly. Calling this for a toast id that no longer exists falls through to a fresh send_toast, so duplicate resolves are harmless.

Examples

# Success
socket = Fluxon.resolve_toast(socket, id, "Done!", color: :success)

# Failure with detail and a retry button
socket =
  Fluxon.resolve_toast(socket, id, "Failed",
    color: :danger,
    description: "Try again.",
    action: %{label: "Retry", type: :event, event: "retry"})

# Keep the loading color, just update the title (no color given)
socket = Fluxon.resolve_toast(socket, id, "Still uploading, 80%...")

resolve_toast(toast_id, title, opts)

resolve_toast(socket, toast_id, title, opts)

send_toast(title)

Shows a rich, in-LiveView toast.

Has three forms - pick by the first argument:

  • send_toast(socket, title, opts) - pushes a channel event from a handle_event / handle_info. Survives the request that emits it but disappears on navigation.
  • send_toast(title, opts) - returns a Phoenix.LiveView.JS command for use in phx-click={...}. Fires a DOM event directly with no server round-trip. The toast payload is rendered server-side at template time, so HEEx in :content / :icon works the same as the socket form.
  • send_toast(%JS{}, title, opts) - same as the bare form but chains onto an existing JS command pipeline (JS.push(...) |> Fluxon.send_toast(...)).

Unlike put_toast/3, send_toast does not survive navigation - pair it with push_navigate/2 or redirect/2 and the toast disappears with the LiveView. Use put_toast/3 for navigation-spanning notifications.

Supports every option of put_toast/3, plus:

  • :content - custom HEEx (~H"...") replacing the title/description block for rich layouts (avatars, multi-line text, custom widgets).
  • :icon - custom HEEx icon, a string of HTML, or false to hide. Strings are rendered via innerHTML; only use values from trusted server-side sources.
  • :action / :actions - may include callback actions (type: :event), link actions (type: :link), and pure-dismiss actions (type: :dismiss). Callbacks push the named :event to your LiveView via phx-event. The default is to dismiss the toast after the action fires; pass dismiss: false to keep it open.

Examples

# Server-driven (handle_event)
Fluxon.send_toast(socket, "Item deleted",
  action: %{label: "Undo", type: :event, event: "undo", value: %{id: 42}})

# Client-driven via phx-click  -  no server round-trip
<.button phx-click={Fluxon.send_toast("Copied!", color: :success)}>
  Copy
</.button>

# Chained: fire a server event AND show an immediate toast
<.button phx-click={
  JS.push("save_draft")
  |> Fluxon.send_toast("Saving...", color: :info, dismiss_after: 2_000)
}>
  Save
</.button>

# Pure-dismiss action  -  closes the toast, no other side effect.
# Pair with dismissible: false so the labeled button is the only
# close control (otherwise the X duplicates it).
Fluxon.send_toast(socket, "Welcome aboard",
  color: :info,
  description: "Tour the dashboard to get started.",
  dismissible: false,
  action: %{label: "Got it", type: :dismiss, position: :inline})

# Rich HEEx content (works in any form  -  the ~H renders at the
# call site)
Fluxon.send_toast(socket, "New follower",
  content: ~H\"""
  <div class="flex items-center gap-2">
    <img src={@user.avatar} class="size-6 rounded-full" />
    <span>{@user.name}</span>
  </div>
  \""")

# Multiple actions, inline layout (Cancel + Retry on one row)
Fluxon.send_toast(socket, "Deploy ready",
  actions: [
    %{label: "Deploy",  type: :event, event: "deploy",  variant: "solid", position: :inline},
    %{label: "Discard", type: :event, event: "discard", variant: "ghost", position: :inline}
  ])

Notes

  • Requires a mounted <.toaster>. The bare and %JS{} forms dispatch a DOM event the toaster hook listens for; if no toaster is on the page (or its LiveView hasn't connected yet) the event has no handler and the toast is dropped.
  • Same :href validation everywhere. Unsafe schemes are rejected at build time regardless of transport.
  • Vanishes on navigation. After push_navigate/2, the toast goes with the LiveView. Use put_toast/3 if the toast must survive.
  • :content / :icon capture assigns at the call site. In the bare/JS forms the ~H is evaluated at template render time, so the content reflects the template's assigns - not whatever assigns are current at the click. Use the socket form if you need to capture click-time assigns.

send_toast(title, opts)

send_toast(socket, title, opts)

send_toast_loading(title)

Shows an indeterminate loading toast, designed to be resolved later via resolve_toast/4.

Has three forms, mirroring send_toast/3:

  • send_toast_loading(socket, title, opts) - pushes a channel event from a handle_event / handle_info. Returns {socket, toast_id} so you can pass the id to resolve_toast/4 when the work completes. Auto-generates an id when :id is missing.
  • send_toast_loading(title, opts) - returns a Phoenix.LiveView.JS command for use in phx-click={...}. The spinner appears with no server round-trip. Requires an explicit :id so the server can resolve it later.
  • send_toast_loading(%JS{}, title, opts) - chains onto an existing JS pipeline (JS.push(...) |> Fluxon.send_toast_loading(...)), so a single click can dispatch a server event AND pop the loading toast at the same instant.

In-LiveView only, like send_toast/3. Defaults to dismissible: false and dismiss_after: nil (persistent), so the toast stays visible until you call resolve_toast/4 or dismiss_toast/2. Both defaults can be overridden via opts. The :color is force-set to :loading.

Examples

# Server-side: returns {socket, id}
def handle_event("upload", _, socket) do
  {socket, id} = Fluxon.send_toast_loading(socket, "Uploading...")
  Task.start(fn -> upload(socket.assigns.user, id, self()) end)
  {:noreply, socket}
end

def handle_info({:upload_done, id}, socket) do
  {:noreply, Fluxon.resolve_toast(socket, id, "Upload complete!", color: :success)}
end

# Custom id (useful when the id needs to match an external record)
{socket, "upload-42"} =
  Fluxon.send_toast_loading(socket, "Uploading file 42...", id: "upload-42")

# Client-kicked: spinner appears instantly on click, server resolves later
<.button phx-click={
  JS.push("upload") |> Fluxon.send_toast_loading("Uploading...", id: "upload-1")
}>
  Upload
</.button>

send_toast_loading(socket, title)

send_toast_loading(socket, title, opts)

toggle_autocomplete(id)

Toggles a autocomplete component by id. Same call shapes as open_autocomplete/1.

toggle_autocomplete(socket, id)

toggle_date_picker(id)

Toggles a date picker component by id. Same call shapes as open_date_picker/1.

toggle_date_picker(socket, id)

toggle_dialog(id)

Toggles a dialog component by id. Same call shapes as open_dialog/1.

toggle_dialog(socket, id)

toggle_dropdown(id)

Toggles a dropdown component by id. Same call shapes as open_dropdown/1.

toggle_dropdown(socket, id)

toggle_popover(id)

Toggles a popover component by id. Same call shapes as open_popover/1.

toggle_popover(socket, id)

toggle_select(id)

Toggles a select component by id. Same call shapes as open_select/1.

toggle_select(socket, id)

toggle_tags_input(id)

Toggles a tags input component by id. Same call shapes as open_tags_input/1.

toggle_tags_input(socket, id)

toggle_tooltip(id)

Toggles a tooltip component by id. Same call shapes as open_tooltip/1.

toggle_tooltip(socket, id)