Fluxon.Components.Dropdown
(Fluxon v3.0.0)
A menu of actions and links anchored to a trigger button, with keyboard navigation, hover intent, and nested submenus.
Dropdown is a compound component family backed by a JavaScript controller. The server renders a toggle, a floating menu panel, and an optional set of sibling submenu panels; the controller positions panels with Floating UI, runs the open and close animations, and tracks a highlighted item per menu. The toggle keeps focus the entire time the menu is open: arrow keys move a visible highlight between items rather than moving DOM focus, so the user never loses their place on the trigger.
The system is composed of pieces designed to be used together:
dropdown/1: the container that mounts the JavaScript hook, renders the toggle, the root menu panel, and any submenu panels declared through the:submenuslot.dropdown_link/1: a navigable menu item rendered as a Phoenix<.link>. Use fornavigate,patch, or plainhrefitems.dropdown_button/1: an action menu item rendered as a<button>. Use forphx-clickhandlers,Phoenix.LiveView.JScommands, and other non-navigation actions.dropdown_submenu_trigger/1: an item that opens a linked:submenupanel. References the submenu by id and shows a right-pointing chevron.dropdown_header/1: a non-interactive label for a group of items.dropdown_separator/1: a thin divider between groups.dropdown_custom/1: an arbitrary content area for avatars, summaries, or any markup that is not a menu item.
A typical structure looks like this:
dropdown
├── :toggle slot (optional; falls back to a default <.button>)
├── dropdown_custom (optional, header card)
├── dropdown_header (optional, group label)
├── dropdown_link / dropdown_button (interactive menu items)
├── dropdown_separator (optional, visual divider)
├── dropdown_submenu_trigger (optional, opens a submenu)
└── :submenu slot (one panel per submenu, matched by `id`)
├── dropdown_link / dropdown_button
└── dropdown_submenu_trigger (submenus can nest indefinitely)Choosing between dropdown and popover
Reach for dropdown when the floating panel is a list of menu items: actions, navigation
targets, or hierarchical groups that the user picks one of. Dropdown builds the menu
pattern for you: a moving highlight, arrow-key navigation between items, and submenu open
and close.
Reach for Fluxon.Components.Popover when the floating panel is arbitrary interactive
content: a settings panel with switches, a filter form, a help blurb. Popover does not
impose menu semantics or roving highlight.
Usage
A dropdown with a default toggle and a few navigation links:
<.dropdown>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/settings"}>Settings</.dropdown_link>
<.dropdown_separator />
<.dropdown_link href={~p"/sign_out"} method="delete">Sign out</.dropdown_link>
</.dropdown>The default toggle renders a Fluxon.Components.Button.button/1 labelled "Menu" with a
chevron icon. Pass label to change the text, or render a fully custom toggle through the
:toggle slot. The menu opens on click and stays anchored to the toggle while it is open.
Mix dropdown_link/1 (navigation) and dropdown_button/1 (actions) freely. Both take the
highlight and participate in keyboard navigation. Activating either kind of item closes the
menu and returns focus to the toggle button:
<.dropdown label="Actions" placement="bottom-end">
<.dropdown_button phx-click="duplicate">Duplicate</.dropdown_button>
<.dropdown_button phx-click="archive">Archive</.dropdown_button>
<.dropdown_separator />
<.dropdown_button phx-click="delete" phx-confirm="Delete this record?" class="text-danger-foreground">
Delete
</.dropdown_button>
</.dropdown>Custom Toggle
When the default <.button> toggle is not the shape you want, use the :toggle slot. The
dropdown renders the slot inside a single focusable element that carries the menu behavior and
its disabled state, so the toggle can take any shape: an avatar pill, an icon button, a
full-width account card. You bring the content and the styling; the dropdown makes it open,
close, and respond to the keyboard like a menu button. Style it entirely through the slot's
class:
<.dropdown>
<:toggle class="flex items-center gap-x-2 rounded-lg p-2 hover:highlight">
<img src={~p"/images/human-avatar-01.png"} alt="User" class="size-6 rounded-lg" />
<span class="text-sm font-semibold">Emma Johnson</span>
<.icon name="hero-chevron-down" class="size-4" />
</:toggle>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/billing"}>Billing</.dropdown_link>
<.dropdown_separator />
<.dropdown_link href={~p"/sign_out"} method="delete">Sign out</.dropdown_link>
</.dropdown>The content must be non-interactive: provide text, icons, images, and layout markup, never a nested button or link, since that would create a second focusable control inside the toggle. The toggle stays a single focusable element with one tab stop. For an icon-only toggle, give it an accessible name with visually hidden text:
<:toggle class="rounded-md p-1.5 hover:highlight">
<.icon name="hero-ellipsis-horizontal" class="size-4" />
<span class="sr-only">Open menu</span>
</:toggle>Use either label or :toggle, not both
When the :toggle slot is provided, label and toggle_class are ignored: the slot
supplies the toggle's content and styling. Provide label only when relying on the default
toggle.
Disabled Items
Disabled items are visually dimmed, skipped by ArrowUp and ArrowDown navigation, and
cannot be activated by click, Enter, or Space. The two kinds of menu items use different
attributes because anchor elements do not support the native disabled attribute:
<.dropdown>
<.dropdown_button>Account</.dropdown_button>
<.dropdown_button disabled>Upgrade plan</.dropdown_button>
<.dropdown_separator />
<.dropdown_link navigate={~p"/settings"}>Settings</.dropdown_link>
<.dropdown_link navigate={~p"/admin"} data-disabled>Admin panel</.dropdown_link>
</.dropdown>| Item | Disable with | Why |
|---|---|---|
dropdown_button/1 | disabled | Native HTML attribute on <button> |
dropdown_link/1 | data-disabled | <a> has no native disabled |
dropdown_submenu_trigger/1 | disabled | Renders as <button> |
Both produce the same visual and behavioural result: dimmed text, no pointer events, and the item is skipped during keyboard navigation.
Rich Content
Combine headers, separators, and custom areas to build menus that go beyond a flat list of
links. Headers, separators, and custom content are non-interactive: they are skipped by
ArrowUp and ArrowDown, and hovering them clears the current item highlight and closes
any open child submenu at the same level.
<.dropdown class="w-64">
<.dropdown_custom class="flex items-center p-2">
<img src={~p"/images/avatar.png"} alt="" class="size-9 rounded-full" />
<div class="flex flex-col ml-3">
<span class="text-sm font-medium">Emma Johnson</span>
<span class="text-xs text-foreground-softer">emma@acme.com</span>
</div>
</.dropdown_custom>
<.dropdown_separator />
<.dropdown_header>Account</.dropdown_header>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/billing"}>Billing</.dropdown_link>
<.dropdown_header>Support</.dropdown_header>
<.dropdown_link navigate={~p"/help"}>Documentation</.dropdown_link>
<.dropdown_link navigate={~p"/contact"}>Contact us</.dropdown_link>
<.dropdown_separator />
<.dropdown_link href={~p"/sign_out"} method="delete" class="text-danger-foreground">
Sign out
</.dropdown_link>
</.dropdown>Custom content can hold focusable elements (buttons, inputs). They are reachable via Tab
but are not part of the menu's roving highlight, so arrow-key navigation still flows
through the surrounding menu items.
Hover Interaction
Set open_on_hover to open the dropdown when the cursor enters the toggle. Moving from the
toggle to the menu panel keeps it open; moving away from both starts the close timer.
hover_open_delay and hover_close_delay add intent buffers to prevent accidental
activation:
<.dropdown open_on_hover hover_open_delay={150} hover_close_delay={250}>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/settings"}>Settings</.dropdown_link>
</.dropdown>Click still toggles the dropdown when open_on_hover is enabled, and keyboard activation
through Enter, Space, ArrowDown, or ArrowUp still works the same way. The hover
delays apply only to the cursor path; keyboard activation is immediate.
Positioning
The placement attribute controls where the menu renders relative to the toggle. The
controller uses Floating UI's flip and shift middleware so the panel automatically
reorients to stay inside the viewport, and size middleware caps the height and turns on
vertical scrolling when content overflows.
<.dropdown placement="bottom-end">
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
</.dropdown>
<.dropdown placement="right-start">
<.dropdown_link navigate={~p"/settings"}>Settings</.dropdown_link>
</.dropdown>| Placement family | When to use |
|---|---|
bottom-* | Default for header bars, toolbars, and form-row menus. |
top-* | Triggers near the bottom of the viewport (footers, sticky bars). |
right-* | Sidebars or vertical menus where the panel should expand sideways. |
left-* | Right-aligned triggers where a right-anchored panel would clip. |
The -start and -end suffixes align the panel to the start or end edge of the trigger
along the cross axis, while plain top, bottom, left, and right center it.
Submenus
Build hierarchical menus with the :submenu slot and dropdown_submenu_trigger/1. The
trigger lives inside the parent menu and references its submenu by id; the submenu panel is
declared as a sibling slot, not nested inside the trigger. The controller renders submenu
panels as DOM siblings of the root menu so they can extend past the parent's boundaries
without being clipped by overflow: hidden.
<.dropdown>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/settings"}>Settings</.dropdown_link>
<.dropdown_separator />
<.dropdown_submenu_trigger submenu="more">More options</.dropdown_submenu_trigger>
<:submenu id="more">
<.dropdown_link navigate={~p"/preferences"}>Preferences</.dropdown_link>
<.dropdown_link navigate={~p"/integrations"}>Integrations</.dropdown_link>
<.dropdown_link navigate={~p"/notifications"}>Notifications</.dropdown_link>
</:submenu>
</.dropdown>Each :submenu slot needs an id that matches the submenu attribute of its trigger. A
trigger with no matching :submenu becomes a regular menu item that does nothing on
ArrowRight, and a :submenu with no trigger is unreachable.
Match submenu and id exactly
The link between trigger and panel is a string equality check on submenu and id.
Typos silently break the relationship: the trigger renders, but pressing ArrowRight or
hovering it does nothing because the controller cannot find the panel.
Submenus default to right-start placement. Override per submenu through the slot's
placement attribute:
<:submenu id="more" placement="left-start">
<!-- Opens to the left of the trigger instead of the right -->
</:submenu>Nested Submenus
Submenus can hold their own dropdown_submenu_trigger/1 items, which reference yet another
:submenu slot. There is no enforced nesting limit; each level opens and closes
independently:
<.dropdown>
<.dropdown_submenu_trigger submenu="file">File</.dropdown_submenu_trigger>
<:submenu id="file">
<.dropdown_button phx-click="new_file">New</.dropdown_button>
<.dropdown_submenu_trigger submenu="export">Export</.dropdown_submenu_trigger>
</:submenu>
<:submenu id="export">
<.dropdown_button phx-click="export" phx-value-format="pdf">PDF</.dropdown_button>
<.dropdown_button phx-click="export" phx-value-format="csv">CSV</.dropdown_button>
</:submenu>
</.dropdown>When a submenu opens, its parent trigger stays highlighted so the user can see where they came from. Hovering a sibling menu item, a header, a separator, or a custom content area closes any open submenu at the same level. Mouse hover on a submenu trigger uses an 80ms intent delay before opening, which prevents accidental activation when the cursor passes diagonally over the trigger on its way to a sibling.
Opening and Closing
The toggle keeps focus the whole time the menu is open, so opening and moving through the
menu never pulls focus away from the trigger. The menu opens when the user clicks the toggle,
presses Enter or Space on it, or presses an arrow key: ArrowDown opens with the first
enabled item highlighted, and ArrowUp opens with the last enabled item highlighted, which
reaches the bottom of a long menu in one keystroke. With open_on_hover set, moving the
cursor onto the toggle opens it too.
While the menu is open, ArrowDown and ArrowUp move the highlight between enabled items.
The highlight does not wrap at the top or bottom, so a held arrow key settles on the first or
last item instead of cycling. Disabled items, headers, separators, and custom content are
skipped.
The menu closes when the user activates an item, clicks the toggle again, clicks outside the
panel, or presses Tab (which lets focus move on naturally). Pressing Escape closes every
open menu and submenu at once. Both Escape and activating an item return focus to the
toggle, so the user lands back where they started.
Examples
Row-action menu in a table, opening above the row on the last few rows so the panel does not get clipped by the page footer:
<.table id="invoices" rows={@invoices}>
<:col :let={invoice} label="Number">{invoice.number}</:col>
<:col :let={invoice} label="Status">{invoice.status}</:col>
<:col :let={invoice} label="">
<.dropdown placement="bottom-end">
<:toggle class="rounded-md p-1.5 hover:highlight">
<.icon name="hero-ellipsis-horizontal" class="size-4" />
<span class="sr-only">Actions for invoice {invoice.number}</span>
</:toggle>
<.dropdown_link navigate={~p"/invoices/#{invoice.id}"}>View</.dropdown_link>
<.dropdown_button phx-click="resend" phx-value-id={invoice.id}>Resend email</.dropdown_button>
<.dropdown_separator />
<.dropdown_button
phx-click="void"
phx-value-id={invoice.id}
phx-confirm="Void this invoice?"
class="text-danger-foreground"
>
Void
</.dropdown_button>
</.dropdown>
</:col>
</.table>User account menu with an avatar header, grouped sections, and a destructive sign-out action separated from the rest:
<.dropdown class="w-64" placement="bottom-end">
<:toggle class="flex items-center gap-2 rounded-full p-1 hover:highlight">
<img src={@current_user.avatar_url} alt="" class="size-8 rounded-full" />
<.icon name="hero-chevron-down" class="size-4 text-foreground-softer" />
</:toggle>
<.dropdown_custom class="flex flex-col gap-0.5 px-2 py-2">
<span class="text-sm font-medium">{@current_user.name}</span>
<span class="text-xs text-foreground-softer">{@current_user.email}</span>
</.dropdown_custom>
<.dropdown_separator />
<.dropdown_header>Account</.dropdown_header>
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
<.dropdown_link navigate={~p"/billing"}>Billing</.dropdown_link>
<.dropdown_header>Workspace</.dropdown_header>
<.dropdown_link navigate={~p"/team"}>Team</.dropdown_link>
<.dropdown_link navigate={~p"/integrations"}>Integrations</.dropdown_link>
<.dropdown_separator />
<.dropdown_link
href={~p"/sign_out"}
method="delete"
class="text-danger-foreground"
>
Sign out
</.dropdown_link>
</.dropdown>Hover-driven nav menu with submenus for a marketing header, where keyboard users can still
click or press Enter on the toggle:
<.dropdown open_on_hover hover_open_delay={120} hover_close_delay={200} label="Products">
<.dropdown_submenu_trigger submenu="cloud">Cloud</.dropdown_submenu_trigger>
<.dropdown_submenu_trigger submenu="self-hosted">Self-hosted</.dropdown_submenu_trigger>
<.dropdown_separator />
<.dropdown_link navigate={~p"/pricing"}>Pricing</.dropdown_link>
<:submenu id="cloud">
<.dropdown_link navigate={~p"/cloud/starter"}>Starter</.dropdown_link>
<.dropdown_link navigate={~p"/cloud/team"}>Team</.dropdown_link>
<.dropdown_link navigate={~p"/cloud/enterprise"}>Enterprise</.dropdown_link>
</:submenu>
<:submenu id="self-hosted">
<.dropdown_link navigate={~p"/self-hosted/community"}>Community</.dropdown_link>
<.dropdown_link navigate={~p"/self-hosted/business"}>Business</.dropdown_link>
</:submenu>
</.dropdown>File menu with Phoenix.LiveView.JS commands wired to actions in the surrounding view:
<.dropdown label="File">
<.dropdown_button phx-click={JS.push("file:new")}>New</.dropdown_button>
<.dropdown_button phx-click={JS.dispatch("editor:save")}>Save</.dropdown_button>
<.dropdown_separator />
<.dropdown_submenu_trigger submenu="export-as">Export as</.dropdown_submenu_trigger>
<:submenu id="export-as">
<.dropdown_button phx-click="export" phx-value-format="pdf">PDF</.dropdown_button>
<.dropdown_button phx-click="export" phx-value-format="csv">CSV</.dropdown_button>
<.dropdown_button phx-click="export" phx-value-format="json">JSON</.dropdown_button>
</:submenu>
<.dropdown_separator />
<.dropdown_button phx-click={JS.dispatch("editor:close")}>Close window</.dropdown_button>
</.dropdown>Server-Side Control
Open, close, or toggle a dropdown from anywhere without a reference to its toggle by giving it
an id and targeting it by command. From a template or another component, dispatch a command
to the dropdown's id with Phoenix.LiveView.JS.dispatch/3:
<.button phx-click={JS.dispatch("fluxon:dropdown:open", to: "#account-menu")}>
Open account menu
</.button>
<.dropdown id="account-menu">
<.dropdown_link navigate={~p"/profile"}>Profile</.dropdown_link>
</.dropdown>The commands are fluxon:dropdown:open, fluxon:dropdown:close, and fluxon:dropdown:toggle.
To drive the same commands from the server, push them prefixed with phx: and pass the target
id in the payload:
def handle_event("sign_out", _params, socket) do
{:noreply, push_event(socket, "fluxon:dropdown:close", %{id: "account-menu"})}
endTo run Phoenix.LiveView.JS commands each time the menu finishes opening or closing, pass
on_open and on_close. They fire after the enter and leave animations complete, which is
useful for syncing external state, toggling a class on the trigger, or notifying the server:
<.dropdown
id="filters"
on_open={JS.push("menu_opened")}
on_close={JS.push("menu_closed")}
>
<.dropdown_button phx-click="reset_filters">Reset filters</.dropdown_button>
</.dropdown>
Summary
Components
Renders the dropdown container, toggle, root menu panel, and any declared submenu panels.
Renders an action menu item as a <button type="button">.
Renders an arbitrary content area inside a dropdown menu.
Renders a non-interactive section label inside a dropdown menu.
Renders a navigable menu item as a Phoenix <.link>.
Renders a thin horizontal divider between groups of dropdown items.
Renders a menu item that opens a linked submenu panel.
Components
Renders the dropdown container, toggle, root menu panel, and any declared submenu panels.
Use this as the wrapper for every dropdown. The default toggle is a Fluxon.Components.Button.button/1
labelled with label and a chevron icon; pass :toggle to render a custom trigger element
instead. The root menu panel and any sibling submenu panels are positioned with Floating UI,
flip and shift to stay on screen, and animate in and out as the user opens or closes them.
Place dropdown_link/1, dropdown_button/1, dropdown_submenu_trigger/1, dropdown_header/1,
dropdown_separator/1, and dropdown_custom/1 inside the inner block. Declare each submenu
panel as a separate :submenu slot whose id matches the submenu attribute on its trigger.
Attributes
id(:string) - Unique identifier for the dropdown root element. Used to derive stable ids for the toggle and menu panel, and as the target when opening, closing, or toggling the dropdown with thefluxon:dropdown:*commands. Generated automatically when not provided.label(:string) - Text rendered inside the default toggle button next to the chevron icon. Ignored when the:toggleslot is provided, since the slot replaces the default button entirely. Defaults to"Menu".Defaults to
"Menu".class(:any) - Additional CSS classes merged into the menu panel. Use this to set the panel width (w-56,w-64), tighten or loosen padding, or override the maximum height. Does not affect the toggle or the outer container.Defaults to
nil.container_class(:string) - Additional CSS classes merged into the outer container element that wraps the toggle and menu panel. Use this when the dropdown needs to participate in a flex or grid row, or to tweakdisplayandpositionon the wrapper.Defaults to
nil.toggle_class(:string) - Additional CSS classes merged into the default toggle button. Has no effect when a custom toggle is supplied through the:toggleslot; style that toggle through the slot's ownclassattribute instead.Defaults to
nil.disabled(:boolean) - Whentrue, the toggle is disabled and the menu cannot be opened by click, hover, or keyboard. Applies whether the toggle is the default button or a custom one supplied through the:toggleslot, dimming it in both cases.Defaults to
false.placement(:string) - Where the menu panel renders relative to the toggle button. The plain side names (top,right,bottom,left) center the panel along the cross axis;-startand-endsuffixes align it to the start or end edge of the toggle. The Floating UIflipandshiftmiddleware automatically reorient the panel when there is not enough room in the requested direction. Defaults to"bottom-start".Accepted values:
top,top-start,top-end,right,right-start,right-end,bottom,bottom-start,bottom-end,left,left-start,left-end.Defaults to
"bottom-start". Must be one of"top","top-start","top-end","right","right-start","right-end","bottom","bottom-start","bottom-end","left","left-start", or"left-end".open_on_hover(:boolean) - Whentrue, the menu opens when the cursor enters the toggle and closes when the cursor leaves both the toggle and the menu panel. Click and keyboard activation continue to work in this mode. Use for nav-style menus where hover discovery matters; leave atfalsefor action menus where accidental opening would be annoying.Defaults to
false.hover_open_delay(:integer) - Milliseconds to wait after the cursor enters the toggle before opening the menu. Only applies whenopen_on_hoveristrue. A small delay (around 100-200ms) prevents the menu from flashing open when the cursor passes over the toggle on its way somewhere else.Defaults to
0.hover_close_delay(:integer) - Milliseconds to wait after the cursor leaves the toggle and panel before closing the menu. Only applies whenopen_on_hoveristrue. A grace period (around 200-300ms) lets the user move the cursor between the toggle and the panel without the menu closing under them.Defaults to
0.on_open(Phoenix.LiveView.JS) -Phoenix.LiveView.JScommands to run after the menu finishes opening (after the enter animation completes).Defaults to
%Phoenix.LiveView.JS{ops: []}.on_close(Phoenix.LiveView.JS) -Phoenix.LiveView.JScommands to run after the menu finishes closing (after the leave animation completes).Defaults to
%Phoenix.LiveView.JS{ops: []}.
Slots
inner_block(required) - The contents of the root menu panel. Holdsdropdown_link/1,dropdown_button/1,dropdown_submenu_trigger/1,dropdown_header/1,dropdown_separator/1, anddropdown_custom/1items in any order.toggle- Content for a custom toggle. The dropdown renders this slot inside a single focusable element that carries the menu behavior and its disabled state, so the toggle can take any shape: an avatar pill, an icon button, a full-width account card, and so on. Style it through the slot'sclass, and provide non-interactive content only (text, icons, images, layout markup), since a nested button or link would create a second focusable control inside the toggle. When this slot is provided,labelandtoggle_classare ignored.Accepts attributes:
class(:any) - CSS classes for the toggle element. Drives its full appearance: shape, color, padding, and radius.
submenu- Declares a submenu panel that opens when its matchingdropdown_submenu_trigger/1is activated. Each:submenuis rendered as a sibling of the root menu panel rather than nested inside the trigger, so the panel can extend past the parent's boundaries without being clipped. The slot'sidmust match thesubmenuattribute on its trigger.Accepts attributes:
id(:string) (required) - Unique identifier for this submenu panel. Must match thesubmenuattribute of the linkeddropdown_submenu_trigger/1.class(:any) - Additional CSS classes merged into the submenu panel. Useful for setting a panel-specific width.placement(:string) - Placement of this submenu relative to its trigger. Accepts the same values as the rootplacementattribute. Defaults to"right-start".
Renders an action menu item as a <button type="button">.
Use this for menu items that fire an action rather than navigating: phx-click handlers,
Phoenix.LiveView.JS commands, dispatched events, and confirm dialogs through
phx-confirm. The item joins the menu's keyboard navigation and takes the highlight as the
user arrows through the list. Clicking or pressing Enter or Space while the item is
highlighted invokes the button's click event and closes the dropdown.
Disable a button item with the standard disabled attribute. Disabled buttons are dimmed,
ignore pointer events, and are skipped during arrow-key navigation.
Examples
<.dropdown_button phx-click="update_view" phx-value-view="grid">Grid</.dropdown_button>
<.dropdown_button phx-click={JS.push("update_view", value: %{view: "list"})}>
List
</.dropdown_button>
<.dropdown_button phx-click={Fluxon.open_dialog("new-user-dialog")}>
New user
</.dropdown_button>
<.dropdown_button
phx-click="delete"
phx-value-id={@record.id}
phx-confirm="Delete this record?"
class="text-danger-foreground"
>
Delete
</.dropdown_button>
<.dropdown_button disabled>Unavailable</.dropdown_button>Attributes
id(:string) - Optional identifier for the button element. When omitted, noidis rendered. Provide a stable id when other code needs to target this specific menu item.Defaults to
nil.class(:any) - Additional CSS classes merged into the button element. Useful for highlighting destructive actions (text-danger-foreground) or for one-off layout tweaks.Defaults to
nil.Global attributes are accepted. Additional HTML attributes forwarded to the underlying
<button>element. Accepts the standard form button attributes plus anyphx-*,data-*, andaria-*attributes. Passdisabledto disable the item. Supports all globals plus:["autofocus", "disabled", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "name", "type", "value"].
Slots
inner_block(required) - The button content. Usually a short label, optionally with an icon. Icons that use the.iconclass are positioned automatically by the menu item layout.
Renders an arbitrary content area inside a dropdown menu.
Use this for content that does not behave like a menu item: a user card at the top of an
account menu, a usage summary with a "Manage" button, an inline form, or a status
indicator. Custom content is skipped by ArrowUp and ArrowDown navigation, so the
surrounding menu items keep their tight vertical flow. Focusable elements inside the area
remain reachable via Tab. Hovering a custom area clears the current item highlight and
closes any open submenu at the same level.
Examples
<.dropdown_custom class="flex items-center p-2">
<img src="https://i.pravatar.cc/150?u=1" alt="" class="size-9 rounded-full" />
<div class="flex flex-col ml-3 mr-10">
<span class="text-sm font-medium leading-snug">Emma Johnson</span>
<span class="text-xs text-foreground-softer leading-snug">emma@acme.com</span>
</div>
<.badge color="danger" class="ml-auto">PRO</.badge>
</.dropdown_custom>
<.dropdown_custom class="flex items-center gap-x-10 bg-accent rounded-lg p-2">
<div class="flex flex-col">
<span class="text-sm font-medium">Available tokens</span>
<span class="text-sm text-foreground-softer">Only 75 tokens available</span>
</div>
<.button size="xs" class="ml-auto" phx-click="open_billing">Manage</.button>
</.dropdown_custom>Attributes
class(:any) - Additional CSS classes merged into the custom container. Use this to set padding, background, or layout (flex,grid) for the embedded content.Defaults to
nil.Global attributes are accepted. Additional HTML attributes forwarded to the underlying container element.
Slots
inner_block(required) - The content to render inside the custom area. Any HEEx markup is allowed, including other Fluxon components. Focusable children stay reachable throughTab.
Renders a non-interactive section label inside a dropdown menu.
Use this to group related menu items under a small caption such as "Account", "Workspace",
or "Support". Headers are skipped by ArrowUp and ArrowDown navigation and cannot be
activated by click or keyboard. Hovering a header clears the current item highlight and
closes any open submenu at the same level, matching the behaviour of separators and custom
content areas.
Examples
<.dropdown_header>Account</.dropdown_header>
<.dropdown_link patch={~p"/profile"}>Edit profile</.dropdown_link>
<.dropdown_link patch={~p"/settings"}>Preferences</.dropdown_link>
<.dropdown_header>Support</.dropdown_header>
<.dropdown_link navigate={~p"/faq"}>FAQ</.dropdown_link>
<.dropdown_link navigate={~p"/contact"}>Contact us</.dropdown_link>Attributes
class(:any) - Additional CSS classes merged into the header element. Useful for changing the typography or spacing of one specific group label without overriding the component's defaults.Defaults to
nil.Global attributes are accepted. Additional HTML attributes forwarded to the underlying
<header>element.
Slots
inner_block(required) - The header content. Usually a short text label but can contain icons or badges for richer section markers.
Renders a navigable menu item as a Phoenix <.link>.
Use this for menu items whose primary effect is navigation: live navigation (navigate),
live patches (patch), external URLs (href), and delete-style sign-out links. The item
joins the menu's keyboard navigation and takes the highlight as the user arrows through the
list. Clicking or activating the item via Enter or Space triggers the link and closes the
dropdown.
Anchor elements do not support the native disabled attribute, so disable a link item with
data-disabled. The disabled state dims the item, removes pointer events, and skips it
during arrow-key navigation.
Examples
<.dropdown_link navigate={~p"/dashboard"}>Dashboard</.dropdown_link>
<.dropdown_link patch={~p"/settings"}>Settings</.dropdown_link>
<.dropdown_link href="https://example.com" target="_blank" rel="noopener">
Documentation
</.dropdown_link>
<.dropdown_link href={~p"/sign_out"} method="delete">Sign out</.dropdown_link>
<.dropdown_link navigate={~p"/admin"} data-disabled>Admin panel</.dropdown_link>Attributes
id(:string) - Optional identifier for the link element. When omitted, noidis rendered. Provide a stable id when other elements need to reference the item, for example througharia-*attributes.Defaults to
nil.class(:any) - Additional CSS classes merged into the link element. Useful for highlighting destructive items (text-danger-foreground) or for tweaking padding on a single row.Defaults to
nil.Global attributes are accepted. Additional HTML attributes forwarded to the underlying
Phoenix.Component.link/1. Accepts LiveView navigation attributes (navigate,patch,replace,method,csrf_token) and standard anchor attributes (href,target,rel,download, ...). Setdata-disabledhere to disable the link. Supports all globals plus:["navigate", "patch", "href", "replace", "method", "csrf_token", "download", "hreflang", "referrerpolicy", "rel", "target", "type"].
Slots
inner_block(required) - The link content. Usually short label text, optionally combined with an icon. Icons that use the.iconclass are positioned automatically by the menu item layout.
Renders a thin horizontal divider between groups of dropdown items.
Use this to separate logically distinct groups inside the same menu, such as the boundary between account links and a destructive sign-out action. The separator is purely decorative: it is skipped by keyboard navigation, and hovering it clears the current item highlight and closes any open submenu at the same level.
Examples
<.dropdown_link patch={~p"/profile"}>Edit profile</.dropdown_link>
<.dropdown_link patch={~p"/settings"}>Preferences</.dropdown_link>
<.dropdown_separator />
<.dropdown_link href={~p"/sign_out"} method="delete">Sign out</.dropdown_link>Attributes
class(:any) - Additional CSS classes merged into the separator element. Useful for adjusting vertical rhythm (my-2,my-3) when the surrounding items feel too tight or too loose.Defaults to
nil.Global attributes are accepted. Additional HTML attributes forwarded to the underlying
<div>element.