Dropdown Menu

Displays a menu to the user triggered by a button with a list of actions or functions.

Requires JavaScript

Installation

Add the component to your project:

rails generate shadcn:component dropdown_menu

Usage

<%= render Shadcn::DropdownMenuComponent.new do |menu| %>
  <% menu.with_trigger do %>
    <%= render Shadcn::ButtonComponent.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(variant: :destructive) { "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.

<!-- Align to start (left) -->
<%= 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 %>

<!-- Align to center -->
<%= 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 %>

<!-- Align to end (right) - default -->
<%= 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 %>

API Reference

DropdownMenuComponent

API Reference

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

API Reference

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-rails", to: "shadcn/index.js"

Then in your app/javascript/controllers/index.js:

import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"

const application = Application.start()
registerShadcnControllers(application)

Or import just this controller:

import DropdownController from "shadcn-rails/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"

Accessibility

  • Uses role="menu" for the dropdown content
  • Sets role="menuitem" on each item
  • Sets aria-expanded on 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-disabled attribute and are skipped in keyboard navigation
  • Automatically closes when clicking outside the dropdown
  • Focus is automatically moved to the first menu item when opened