Maui.Dialog (maui v1.0.0-alpha.9)

Copy Markdown

A modal dialog component for LiveView applications.

Basic Usage

The simplest way to use a dialog is with a trigger button:

<.dialog id="my-dialog">
  <:trigger :let={attr}>
    <.button {attr}>Open Dialog</.button>
  </:trigger>
  <p>Dialog content goes here.</p>
</.dialog>

Accessing Hide/Show Actions

Use :let to access the hide and show JS commands:

<.dialog :let={%{hide: hide, show: show}} id="my-dialog">
  <:trigger :let={attr}>
    <.button {attr}>Open</.button>
  </:trigger>
  <p>Content</p>
  <.button phx-click={hide}>Close</.button>
</.dialog>

Server-Controlled Dialog

Control dialog visibility from your LiveView using the show attribute:

# In your LiveView
def mount(_params, _session, socket) do
  {:ok, assign(socket, show_dialog: false)}
end

def handle_event("open", _, socket), do: {:noreply, assign(socket, show_dialog: true)}
def handle_event("close", _, socket), do: {:noreply, assign(socket, show_dialog: false)}

# In template - use on_cancel to sync state when dismissed via backdrop/escape
<.button phx-click="open">Open</.button>
<.dialog id="my-dialog" show={@show_dialog} on_cancel={JS.push("close")}>
  <p>Server-controlled content</p>
  <.button phx-click="close">Close</.button>
</.dialog>

show={} vs :if={}

Two approaches for server-controlled dialogs:

ApproachBehavior
show={@visible}Dialog stays in DOM, visibility toggled. Form state preserved, animations work.
:if={@visible}Dialog mounted/unmounted. Form state resets, no exit animations.

Using show={} (recommended for most cases):

<.dialog id="dialog" show={@show_dialog} on_cancel={JS.push("close")}>
  ...
</.dialog>

Using :if={} (when you want fresh state each time):

<.dialog :if={@show_dialog} id="dialog" show={true} on_cancel={JS.push("close")}>
  ...
</.dialog>

Alert Dialog

Use alert={true} to prevent closing via backdrop click (escape still works):

<.dialog id="confirm-delete" alert={true}>
  <:trigger :let={attr}>
    <.button variant="destructive" {attr}>Delete</.button>
  </:trigger>
  <p>Are you sure? This cannot be undone.</p>
</.dialog>

Dialog Sizes

Control max-width with the size attribute:

<.dialog id="small" size="sm">...</.dialog>   # sm:max-w-sm
<.dialog id="medium" size="md">...</.dialog>  # md:max-w-md (default)
<.dialog id="large" size="lg">...</.dialog>   # lg:max-w-lg
<.dialog id="xlarge" size="xl">...</.dialog>  # xl:max-w-xl

Custom Content Slot

Override the default content container for full customization:

<.dialog id="custom">
  <:trigger :let={attr}>
    <.button {attr}>Open</.button>
  </:trigger>
  <:content :let={{attrs, %{hide: hide}}}>
    <div class="my-custom-dialog-class" {attrs}>
      <p>Fully customized container</p>
      <.button phx-click={hide}>Close</.button>
    </div>
  </:content>
</.dialog>

Programmatic Show/Hide

Use show_dialog/1 and hide_dialog/1 functions directly:

<.button phx-click={Maui.Dialog.show_dialog("my-dialog")}>Open</.button>
<.button phx-click={Maui.Dialog.hide_dialog("my-dialog")}>Close</.button>

Attributes

AttributeTypeDefaultDescription
idstringrequiredUnique identifier for the dialog
showbooleanfalseControl visibility from server
alertbooleanfalsePrevent backdrop click dismiss
sizestring"md"Max width: "sm", "md", "lg", "xl"
on_cancelJS%JS{}JS command to run on cancel

Slots

SlotDescription
inner_blockMain dialog content
triggerButton/element to open dialog (receives phx-click attr)
contentOverride content container (receives attrs and hide/show)

Summary

Functions

backdrop(assigns)

Attributes

  • class (:string) - Defaults to "".
  • Global attributes are accepted.

Slots

  • inner_block

content(assigns)

Attributes

  • id (:string) (required)
  • class (:string) - Defaults to "".
  • Global attributes are accepted.

Slots

  • inner_block

dialog(assigns)

Attributes

  • id (:string) (required)
  • on_cancel (Phoenix.LiveView.JS) - Defaults to %Phoenix.LiveView.JS{ops: []}.
  • alert (:boolean) - Defaults to false.
  • show (:boolean) - Control dialog visibility from server. Defaults to false.
  • size (:string) - Defaults to "md". Must be one of "sm", "md", "lg", or "xl".

Slots

  • inner_block
  • trigger
  • content - To override the content container.

hide_dialog(id)

show_dialog(id)