Dropdown Menu
Displays a menu to the user triggered by a button with a list of actions or functions.
<%= render(Shadcn::DropdownMenu.new) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Menu" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "My Account" } %>
<% content.with_separator %>
<% content.with_item(href: "#profile") { "Profile" } %>
<% content.with_item(href: "#settings") { "Settings" } %>
<% content.with_separator %>
<% content.with_item { "Log out" } %>
<% end %>
<% end %>
Installation
Add the component to your project:
rails generate shadcn:add dropdown_menu
Usage
<%= render Shadcn::DropdownMenu.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Menu" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "My Account" } %>
<% content.with_separator %>
<% content.with_item(href: "#profile") { "Profile" } %>
<% content.with_item(href: "#settings") { "Settings" } %>
<% content.with_separator %>
<% content.with_item { "Log out" } %>
<% end %>
<% end %>
Examples
Basic
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Options" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Edit" } %>
<% content.with_item(href: "#") { "Duplicate" } %>
<% content.with_item(href: "#") { "Archive" } %>
<% end %>
<% end %>
With Icons
Add icons to menu items for better visual clarity.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Actions" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
Edit
<% end %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
Copy
<% end %>
<% content.with_item(href: "#") do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>
Share
<% end %>
<% end %>
<% end %>
With Separators and Labels
Use separators and labels to organize menu items into logical groups.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Settings" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "Account" } %>
<% content.with_separator %>
<% content.with_item(href: "#") { "Profile" } %>
<% content.with_item(href: "#") { "Billing" } %>
<% content.with_item(href: "#") { "Notifications" } %>
<% content.with_separator %>
<% content.with_label { "Danger Zone" } %>
<% content.with_separator %>
<% content.with_item(variant: :destructive, href: "#") { "Delete Account" } %>
<% end %>
<% end %>
With Keyboard Shortcuts
Display keyboard shortcuts to help users discover shortcuts.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "File" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") do |item| %>
New File
<% item.with_shortcut { "⌘N" } %>
<% end %>
<% content.with_item(href: "#") do |item| %>
Open
<% item.with_shortcut { "⌘O" } %>
<% end %>
<% content.with_item(href: "#") do |item| %>
Save
<% item.with_shortcut { "⌘S" } %>
<% end %>
<% content.with_separator %>
<% content.with_item(href: "#") do |item| %>
Print
<% item.with_shortcut { "⌘P" } %>
<% end %>
<% end %>
<% end %>
Destructive Items
Use the destructive variant for dangerous actions.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Manage" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "View Details" } %>
<% content.with_item(href: "#") { "Edit" } %>
<% content.with_separator %>
<% content.with_item(variant: :destructive, href: "#") { "Delete" } %>
<% end %>
<% end %>
Disabled Items
Disable menu items when an action is unavailable.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Options" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Edit" } %>
<% content.with_item(href: "#", disabled: true) { "Share (Coming Soon)" } %>
<% content.with_item(href: "#") { "Duplicate" } %>
<% end %>
<% end %>
Inset Items
Use the inset prop to add left padding for better alignment with icons.
<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Menu with Icons" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label(inset: true) { "Actions" } %>
<% content.with_separator %>
<% content.with_item(href: "#", inset: true) do %>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>
Edit
<% end %>
<% content.with_item(href: "#", inset: true) { "Delete (no icon)" } %>
<% end %>
<% end %>
Alignment
Control the alignment of the dropdown menu content relative to the trigger.
<div class="flex gap-4 justify-between">
<%= render Shadcn::DropdownMenuComponent.new(align: :start) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Align Start" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Item 1" } %>
<% content.with_item(href: "#") { "Item 2" } %>
<% end %>
<% end %>
<%= render Shadcn::DropdownMenuComponent.new(align: :center) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Align Center" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Item 1" } %>
<% content.with_item(href: "#") { "Item 2" } %>
<% end %>
<% end %>
<%= render Shadcn::DropdownMenuComponent.new(align: :end) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::ButtonComponent.new(variant: :outline) { "Align End" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#") { "Item 1" } %>
<% content.with_item(href: "#") { "Item 2" } %>
<% end %>
<% end %>
</div>
With Shortcuts
<%= render(Shadcn::DropdownMenu.new) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Actions" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "Actions" } %>
<% content.with_separator %>
<% content.with_item(href: "#new") do |item| %>
<% item.with_shortcut { "⌘N" } %>
"New File"
<% end %>
<% content.with_item(href: "#open") do |item| %>
<% item.with_shortcut { "⌘O" } %>
"Open File"
<% end %>
<% content.with_item(href: "#save") do |item| %>
<% item.with_shortcut { "⌘S" } %>
"Save"
<% end %>
<% end %>
<% end %>
Destructive Actions
<%= render(Shadcn::DropdownMenu.new) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "More Options" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_item(href: "#edit") { "Edit" } %>
<% content.with_item(href: "#duplicate") { "Duplicate" } %>
<% content.with_separator %>
<% content.with_item(variant: :destructive, href: "#delete") { "Delete" } %>
<% content.with_item(variant: :destructive, href: "#archive") { "Archive" } %>
<% end %>
<% end %>
With Inset
<%= render(Shadcn::DropdownMenu.new) do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "View Options" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label(inset: true) { "View" } %>
<% content.with_separator %>
<% content.with_item(inset: true, href: "#") { "Show Sidebar" } %>
<% content.with_item(inset: true, href: "#") { "Show Toolbar" } %>
<% content.with_item(inset: true, href: "#") { "Show Status Bar" } %>
<% end %>
<% end %>
User Menu Example
<%= render Shadcn::DropdownMenu.new do |menu| %>
<% menu.with_trigger do %>
<%= render Shadcn::Button.new(variant: :outline) { "Open Menu" } %>
<% end %>
<% menu.with_menu do |content| %>
<% content.with_label { "My Account" } %>
<% content.with_separator %>
<% content.with_item(href: "#profile") { "Profile" } %>
<% content.with_item(href: "#settings") { "Settings" } %>
<% content.with_separator %>
<% content.with_item { "Log out" } %>
<% end %>
<% end %>
API Reference
DropdownMenuComponent
| Prop | Type | Default | Description |
|---|---|---|---|
| open |
Boolean
|
false
|
Whether the dropdown starts in open state |
| align |
Symbol
|
:end
|
Content alignment (:start, :center, :end) |
| side |
Symbol
|
:bottom
|
Side to show content (:top, :right, :bottom, :left) |
Slots
| Slot | Description |
|---|---|
| with_trigger | The element that opens the dropdown (usually a button) |
| with_menu | The dropdown menu content container (yields DropdownMenuContentComponent) |
DropdownMenuContentComponent Slots
| Slot | Props | Description |
|---|---|---|
| with_item | href:, variant:, disabled:, inset: |
Menu item (can be a link or div) |
| with_label | inset: |
Section label for grouping items |
| with_separator | - | Visual separator between menu sections |
| with_group | - | Group of related items |
DropdownMenuItemComponent
| Prop | Type | Default | Description |
|---|---|---|---|
| href |
String
|
nil
|
Link URL (renders as <a> if provided, <div> otherwise) |
| variant |
Symbol
|
:default
|
Item variant (:default, :destructive) |
| disabled |
Boolean
|
false
|
Whether the item is disabled |
| inset |
Boolean
|
false
|
Whether to add left padding for icon alignment |
DropdownMenuItemComponent Slots
| Slot | Description |
|---|---|
| with_shortcut | Keyboard shortcut hint displayed on the right |
Stimulus Controller
This component requires JavaScript. The Stimulus controller shadcn--dropdown provides interactivity.
Manages dropdown menu open/close state, positioning, keyboard navigation, and item selection.
Installation
Add to your config/importmap.rb:
pin "shadcn", to: "index.esm.js"
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
yarn add shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Install the package:
npm install shadcn-rails-stimulus
Then in your app/javascript/controllers/index.js:
import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails-stimulus"
const application = Application.start()
registerShadcnControllers(application)
Or import just this controller:
import DropdownController from "shadcn-rails-stimulus/controllers/dropdown_controller"
application.register("shadcn--dropdown", DropdownController)
Targets
| Name | Description |
|---|---|
| trigger | The element that triggers the dropdown to open |
| content | The dropdown menu content container |
| item | Individual menu items for keyboard navigation |
Values
| Name | Type | Default | Description |
|---|---|---|---|
| open | Boolean |
false
|
Current open/closed state |
| align | String |
end
|
Content alignment (start, center, end) |
| side | String |
bottom
|
Side to show content (top, bottom, left, right) |
Actions
| Action | Description |
|---|---|
| toggle | Toggles the dropdown open/closed state |
| show | Opens the dropdown menu |
| hide | Closes the dropdown menu |
| close | Alias for hide |
| selectItem | Handles item selection and closes menu |
TypeScript
Type definitions are included. Import types as needed:
import type { DropdownController } from "shadcn-rails-stimulus"
Accessibility
- Uses
role="menu"for the dropdown content - Sets
role="menuitem"on each item - Sets
aria-expandedon trigger to indicate open state - Supports full keyboard navigation:
- Escape - Closes the menu and returns focus to trigger
- ↓ / ↑ - Navigate through menu items
- Home / End - Jump to first/last item
- Enter / Space - Select focused item
- Disabled items use
data-disabledattribute and are skipped in keyboard navigation - Automatically closes when clicking outside the dropdown
- Focus is automatically moved to the first menu item when opened
On This Page