Blocks

Dropdown Menu

Displays a list of actions or links that appear below a trigger button.

Installation

bin/rails g shadcnrb:component dropdown_menu

Usage

Compose m.trigger and m.content inside sui.dropdown_menu do |m|. The open/close behaviour is handled by the bundled Stimulus controller (shadcnrb--dropdown-menu--component) which is auto-installed with the gem.

<%= sui.dropdown_menu do |m| %>
  <%= m.trigger "Open menu" %>
  <%= m.content do %>
    <%= m.label "My Account" %>
    <%= m.separator %>
    <%= m.item "Profile",  icon: :user %>
    <%= m.item "Settings", icon: :settings %>
    <%= m.separator %>
    <%= m.item "Sign out", icon: :x %>
  <% end %>
<% end %>

With links

Pass href: to m.item to render an <a> tag instead of a button.

<%= sui.dropdown_menu do |m| %>
  <%= m.trigger "Navigate", variant: :default %>
  <%= m.content do %>
    <%= m.item "Dashboard", href: "#" %>
    <%= m.item "Billing",   href: "#" %>
    <%= m.separator %>
    <%= m.item "Log out",   href: "#" %>
  <% end %>
<% end %>

Nested sub-menu

m.sub do |sub| spawns a nested menu: sub.trigger looks like a regular item with a right-chevron, and sub.content floats to the right. Opens on hover, closes after a short delay when the pointer leaves both. Clicking a leaf item cascades-closes every ancestor.

<%= sui.dropdown_menu do |m| %>
  <%= m.trigger "File", variant: :outline %>
  <%= m.content do %>
    <%= m.item "New file",  "#" %>
    <%= m.item "Open…",     "#" %>
    <%= m.sub do |sub| %>
      <%= sub.trigger "Share" %>
      <%= sub.content do %>
        <%= m.item "Email link", "#" %>
        <%= m.item "Copy link",  "#" %>
        <%= m.sub do |deeper| %>
          <%= deeper.trigger "More…" %>
          <%= deeper.content do %>
            <%= m.item "Slack",   "#" %>
            <%= m.item "Discord", "#" %>
          <% end %>
        <% end %>
      <% end %>
    <% end %>
    <%= m.separator %>
    <%= m.item "Save",     "#" %>
    <%= m.item "Save as…", "#" %>
  <% end %>
<% end %>

Composition — scoped helpers inside

Pass a block with a scope arg (m.item do |i|) and use i.link_to / i.button_to inside. The item's scope tells link_to / button_to they're in a non-top-level context, so they default to variant: :bare automatically — no stacked button visuals over the row styling. CSS on m.item ([&>a]:flex-1 [&>form]:flex-1) makes the inner element fill the row so clicks land anywhere.

<%= sui.dropdown_menu do |m| %>
  <%= m.trigger "Account", variant: :default %>
  <%= m.content do %>
    <%= m.item do |i| %>
      <%= i.link_to "Profile", "#", icon: :user %>
    <% end %>
    <%= m.item do |i| %>
      <%= i.link_to "Settings", "#", icon: :settings %>
    <% end %>
    <%= m.separator %>
    <%= m.item do |i| %>
      <%= i.button_to "Sign out", "#", method: :delete, icon: :x %>
    <% end %>
  <% end %>
<% end %>

API

MethodArgsDefaultDescription
sui.dropdown_menuRoot container. Mounts the shadcnrb--dropdown-menu--component Stimulus controller and yields an m proxy.
m.trigger(name)variant:, size::outline, :defaultButton that toggles the menu. Accepts all sui.button options.
m.contentFloating panel that appears below the trigger. Hidden by default; shown when state is open.
m.label(name)Non-interactive section heading inside the content panel.
m.separatorHorizontal divider line between groups of items.
m.item(name)href:, &blocknilInteractive row. Shortcut renders <button> (or <a> when href: is given). Block form renders a <div role="menuitem"> wrapper — put link_to / button_to inside. Closes menu on click.