Dropdown Menu
DropdownMenu - Phlex implementation
Examples
Basic
Displays a menu to the user—such as a set of actions or functions—triggered by a button.
<%= render "ui/dropdown_menu" do %>
<%= render "ui/dropdown_menu/trigger", as_child: true do |attrs| %>
<%= render "ui/button", variant: :outline, attributes: attrs do %>Open Menu<% end %>
<% end %>
<%= render "ui/dropdown_menu/content", classes: "w-56" do %>
<%= render "ui/dropdown_menu/label" do %>My Account<% end %>
<%= render "ui/dropdown_menu/separator" %>
<%= render "ui/dropdown_menu/item" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
Profile
<%= render "ui/dropdown_menu/shortcut" do %>⇧⌘P<% end %>
<% end %>
<%= render "ui/dropdown_menu/item" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-credit-card"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg>
Billing
<%= render "ui/dropdown_menu/shortcut" do %>⌘B<% end %>
<% end %>
<%= render "ui/dropdown_menu/item" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
Settings
<%= render "ui/dropdown_menu/shortcut" do %>⌘S<% end %>
<% end %>
<%= render "ui/dropdown_menu/separator" %>
<%= render "ui/dropdown_menu/item" do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
Log out
<%= render "ui/dropdown_menu/shortcut" do %>⇧⌘Q<% end %>
<% end %>
<% end %>
<% end %><%= render UI::DropdownMenu.new do %>
<%= render UI::DropdownMenuTrigger.new(as_child: true) do |attrs| %>
<%= render UI::Button.new(**attrs, variant: :outline) { "Open Menu" } %>
<% end %>
<%= render UI::DropdownMenuContent.new(classes: "w-56") do %>
<%= render UI::DropdownMenuLabel.new { "My Account" } %>
<%= render UI::DropdownMenuSeparator.new %>
<%= render UI::DropdownMenuItem.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
Profile
<%= render UI::DropdownMenuShortcut.new { "⇧⌘P" } %>
<% end %>
<%= render UI::DropdownMenuItem.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-credit-card"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg>
Billing
<%= render UI::DropdownMenuShortcut.new { "⌘B" } %>
<% end %>
<%= render UI::DropdownMenuItem.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
Settings
<%= render UI::DropdownMenuShortcut.new { "⌘S" } %>
<% end %>
<%= render UI::DropdownMenuSeparator.new %>
<%= render UI::DropdownMenuItem.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
Log out
<%= render UI::DropdownMenuShortcut.new { "⇧⌘Q" } %>
<% end %>
<% end %>
<% end %><%= render UI::DropdownMenuComponent.new do %>
<%= render UI::DropdownMenuTriggerComponent.new(as_child: true) do %>
<%= render(UI::ButtonComponent.new(variant: :outline)) { "Open Menu" } %>
<% end %>
<%= render UI::DropdownMenuContentComponent.new(classes: "w-56") do %>
<%= render(UI::DropdownMenuLabelComponent.new) { "My Account" } %>
<%= render UI::DropdownMenuSeparatorComponent.new %>
<%= render UI::DropdownMenuItemComponent.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
Profile
<%= render(UI::DropdownMenuShortcutComponent.new) { "⇧⌘P" } %>
<% end %>
<%= render UI::DropdownMenuItemComponent.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-credit-card"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg>
Billing
<%= render(UI::DropdownMenuShortcutComponent.new) { "⌘B" } %>
<% end %>
<%= render UI::DropdownMenuItemComponent.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-settings"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>
Settings
<%= render(UI::DropdownMenuShortcutComponent.new) { "⌘S" } %>
<% end %>
<%= render UI::DropdownMenuSeparatorComponent.new %>
<%= render UI::DropdownMenuItemComponent.new do %>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-log-out"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>
Log out
<%= render(UI::DropdownMenuShortcutComponent.new) { "⇧⌘Q" } %>
<% end %>
<% end %>
<% end %>With Checkboxes
A dropdown menu with checkbox items for toggling options.
<%= render "ui/dropdown_menu" do %>
<%= render "ui/dropdown_menu/trigger", as_child: true do |attrs| %>
<%= render "ui/button", variant: :outline, attributes: attrs do %>View Options<% end %>
<% end %>
<%= render "ui/dropdown_menu/content", classes: "w-56" do %>
<%= render "ui/dropdown_menu/label" do %>Appearance<% end %>
<%= render "ui/dropdown_menu/separator" %>
<%= render "ui/dropdown_menu/checkbox_item", checked: true do %>Status Bar<% end %>
<%= render "ui/dropdown_menu/checkbox_item", checked: false do %>Activity Bar<% end %>
<%= render "ui/dropdown_menu/checkbox_item", checked: true do %>Panel<% end %>
<% end %>
<% end %><%= render UI::DropdownMenu.new do %>
<%= render UI::DropdownMenuTrigger.new(as_child: true) do |attrs| %>
<%= render UI::Button.new(**attrs, variant: :outline) { "View Options" } %>
<% end %>
<%= render UI::DropdownMenuContent.new(classes: "w-56") do %>
<%= render UI::DropdownMenuLabel.new { "Appearance" } %>
<%= render UI::DropdownMenuSeparator.new %>
<%= render UI::DropdownMenuCheckboxItem.new(checked: true) { "Status Bar" } %>
<%= render UI::DropdownMenuCheckboxItem.new(checked: false) { "Activity Bar" } %>
<%= render UI::DropdownMenuCheckboxItem.new(checked: true) { "Panel" } %>
<% end %>
<% end %><%= render UI::DropdownMenuComponent.new do %>
<%= render UI::DropdownMenuTriggerComponent.new(as_child: true) do %>
<%= render(UI::ButtonComponent.new(variant: :outline)) { "View Options" } %>
<% end %>
<%= render UI::DropdownMenuContentComponent.new(classes: "w-56") do %>
<%= render(UI::DropdownMenuLabelComponent.new) { "Appearance" } %>
<%= render UI::DropdownMenuSeparatorComponent.new %>
<%= render(UI::DropdownMenuCheckboxItemComponent.new(checked: true)) { "Status Bar" } %>
<%= render(UI::DropdownMenuCheckboxItemComponent.new(checked: false)) { "Activity Bar" } %>
<%= render(UI::DropdownMenuCheckboxItemComponent.new(checked: true)) { "Panel" } %>
<% end %>
<% end %>With Radio Group
A dropdown menu with radio items for selecting a single option.
<%= render "ui/dropdown_menu" do %>
<%= render "ui/dropdown_menu/trigger", as_child: true do |attrs| %>
<%= render "ui/button", variant: :outline, attributes: attrs do %>Panel Position<% end %>
<% end %>
<%= render "ui/dropdown_menu/content", classes: "w-56" do %>
<%= render "ui/dropdown_menu/label" do %>Position<% end %>
<%= render "ui/dropdown_menu/separator" %>
<%= render "ui/dropdown_menu/radio_group" do %>
<%= render "ui/dropdown_menu/radio_item", value: "top" do %>Top<% end %>
<%= render "ui/dropdown_menu/radio_item", value: "bottom", checked: true do %>Bottom<% end %>
<%= render "ui/dropdown_menu/radio_item", value: "right" do %>Right<% end %>
<% end %>
<% end %>
<% end %><%= render UI::DropdownMenu.new do %>
<%= render UI::DropdownMenuTrigger.new(as_child: true) do |attrs| %>
<%= render UI::Button.new(**attrs, variant: :outline) { "Panel Position" } %>
<% end %>
<%= render UI::DropdownMenuContent.new(classes: "w-56") do %>
<%= render UI::DropdownMenuLabel.new { "Position" } %>
<%= render UI::DropdownMenuSeparator.new %>
<%= render UI::DropdownMenuRadioGroup.new do %>
<%= render UI::DropdownMenuRadioItem.new(value: "top") { "Top" } %>
<%= render UI::DropdownMenuRadioItem.new(value: "bottom", checked: true) { "Bottom" } %>
<%= render UI::DropdownMenuRadioItem.new(value: "right") { "Right" } %>
<% end %>
<% end %>
<% end %><%= render UI::DropdownMenuComponent.new do %>
<%= render UI::DropdownMenuTriggerComponent.new(as_child: true) do %>
<%= render(UI::ButtonComponent.new(variant: :outline)) { "Panel Position" } %>
<% end %>
<%= render UI::DropdownMenuContentComponent.new(classes: "w-56") do %>
<%= render(UI::DropdownMenuLabelComponent.new) { "Position" } %>
<%= render UI::DropdownMenuSeparatorComponent.new %>
<%= render UI::DropdownMenuRadioGroupComponent.new do %>
<%= render(UI::DropdownMenuRadioItemComponent.new(value: "top")) { "Top" } %>
<%= render(UI::DropdownMenuRadioItemComponent.new(value: "bottom", checked: true)) { "Bottom" } %>
<%= render(UI::DropdownMenuRadioItemComponent.new(value: "right")) { "Right" } %>
<% end %>
<% end %>
<% end %>Features
- Custom styling with Tailwind classes
API Reference
Dropdown Menu
Container for dropdown menus with Stimulus controller for interactivity.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| as_child | Boolean | false | When true, yields attributes to block instead of rendering wrapper |
| placement | String | bottom-start | The placement |
| offset | Integer | 4 | The offset |
| flip | Boolean | true | The flip |
Content
Menu items container with animations and positioning.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| side_offset | Integer | 4 | The side offset |
Item
Individual menu item that can be rendered as a link or div.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| href | String | nil | The href |
| inset | Boolean | false | The inset |
| variant | String | default | Visual style variant |
Label
Label for menu sections to organize items.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| inset | Boolean | false | The inset |
Separator
Visual separator between menu items.
Shortcut
Keyboard shortcut indicator displayed at the end of menu items.
Sub
Container for submenu with relative positioning.
Sub Content
Submenu items container positioned to the right of the trigger.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| side | String | right | Which side to display on |
| align | String | start | Alignment within container |
Sub Trigger
Item that opens a submenu on hover.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| inset | Boolean | false | The inset |
Trigger
Wrapper that adds toggle action to child element.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
| as_child | Boolean | false | When true, yields attributes to block instead of rendering wrapper |
Accessibility
Implements the WAI-ARIA Menu Button pattern with proper roles, states, and keyboard navigation.