Rapid Rails UI Pro

—

Upgrade to unlock this component

Get Pro →

Dropdown

Versatile dropdown menu for navigation, actions, and form selection with full accessibility support.

Key Features

  • Two Modes - Action dropdown (navigation) and Selection dropdown (form binding)
  • 4 Variants - Solid, Outline, Ghost, Soft
  • 6 Colors - Zinc, Primary, Success, Warning, Danger, Info
  • 5 Sizes - xs, sm, base, lg, xl
  • 3 Shapes - Square, Rounded, Pill
  • 5 Menu Widths - sm, base, lg, xl, full
  • 4 Placements - Top, Right, Bottom, Left
  • Slots - Items, Headers, Dividers, Icon
  • Collections - Hash, Array, ActiveRecord support
  • Active State - Mark current page items with custom styling NEW
  • Form Builder - Full Rails form integration (f.rui_dropdown)
  • Keyboard Navigation - Arrow keys, Escape, Enter
  • Full Accessibility - ARIA attributes, focus management
  • JavaScript API - Programmatic control with data-action attributes

Action Dropdown

Use action dropdowns for navigation menus, action lists, or command palettes. The button text stays unchanged when items are clicked.

Action Dropdown
<%= rui_dropdown(text: "Actions", color: :orange) do |dropdown| %>
  <% dropdown.with_item(text: "Edit", href: "#") %>
  <% dropdown.with_item(text: "Duplicate", href: "#") %>
  <% dropdown.with_divider %>
  <% dropdown.with_item(text: "Delete", href: "#", danger: true) %>
<% end %>

<%= rui_dropdown(text: "Settings", variant: :outline, color: :orange) do |dropdown| %>
  <% dropdown.with_header(text: "Account") %>
  <% dropdown.with_item(text: "Profile", href: "#") %>
  <% dropdown.with_item(text: "Preferences", href: "#") %>
  <% dropdown.with_divider %>
  <% dropdown.with_header(text: "System") %>
  <% dropdown.with_item(text: "Logout", href: "#", danger: true) %>
<% end %>

Selection Dropdown

Use selection dropdowns as form inputs. When an item is selected, the button text updates and a hidden input stores the value.

Selection Dropdown
<%= rui_dropdown(
  :category,
  collection: { "Technology" => "tech", "Design" => "design", "Business" => "business" },
  label: "Category",
  help_text: "Select a category for your post"
) %>

<%= rui_dropdown(
  :priority,
  collection: [["Low", "low"], ["Medium", "medium"], ["High", "high"]],
  label: "Priority",
  color: :info,
  required: true
) %>

Variants

Four button style variants for different visual contexts.

Variants
<%= rui_dropdown(text: "Solid", variant: :solid, color: :primary) %>
<%= rui_dropdown(text: "Outline", variant: :outline, color: :primary) %>
<%= rui_dropdown(text: "Ghost", variant: :ghost, color: :primary) %>
<%= rui_dropdown(text: "Soft", variant: :soft, color: :primary) %>

Colors

Semantic colors for different purposes. The brand colors (:primary, :secondary, :tertiary, :accent) resolve through your app's config.colors, including the trigger's focus ring; status colors are fixed.

Colors
<%= rui_dropdown(text: "Zinc", color: :zinc) %>
<%= rui_dropdown(text: "Primary", color: :primary) %>
<%= rui_dropdown(text: "Success", color: :success) %>
<%= rui_dropdown(text: "Warning", color: :warning) %>
<%= rui_dropdown(text: "Danger", color: :danger) %>
<%= rui_dropdown(text: "Info", color: :info) %>

Tailwind Colors

All standard Tailwind colors are supported with automatic dark mode variants.

Tailwind Colors
<%= rui_dropdown(text: "Red", color: :red) %>
<%= rui_dropdown(text: "Orange", color: :orange) %>
<%= rui_dropdown(text: "Yellow", color: :yellow) %>
<%= rui_dropdown(text: "Green", color: :green) %>
<%= rui_dropdown(text: "Blue", color: :blue) %>
<%= rui_dropdown(text: "Purple", color: :purple) %>
<%= rui_dropdown(text: "Pink", color: :pink) %>

Sizes

Five size options from extra small to extra large.

Sizes
<%= rui_dropdown(text: "XS", size: :xs) %>
<%= rui_dropdown(text: "SM", size: :sm) %>
<%= rui_dropdown(text: "Base", size: :base) %>
<%= rui_dropdown(text: "LG", size: :lg) %>
<%= rui_dropdown(text: "XL", size: :xl) %>

Shapes

Three shape options for button corners.

Shapes
<%= rui_dropdown(text: "Square", shape: :square) %>
<%= rui_dropdown(text: "Rounded", shape: :rounded) %>
<%= rui_dropdown(text: "Pill", shape: :pill) %>

Menu Widths

Control the width of the dropdown menu.

Menu Widths
<%= rui_dropdown(text: "Small", width: :sm) %>
<%= rui_dropdown(text: "Base", width: :base) %>
<%= rui_dropdown(text: "Large", width: :lg) %>
<%= rui_dropdown(text: "XL", width: :xl) %>
<%= rui_dropdown(text: "Full", width: :full) %>

Placements

Position the dropdown menu relative to the trigger button.

Placements
<%= rui_dropdown(text: "Bottom", placement: :bottom) %>
<%= rui_dropdown(text: "Top", placement: :top) %>
<%= rui_dropdown(text: "Right", placement: :right) %>
<%= rui_dropdown(text: "Left", placement: :left) %>

Trigger Types

Control how the dropdown opens: on click (default) or on hover.

Note: Hover trigger includes a 150ms delay before opening and 200ms delay before closing to prevent accidental triggers. This follows Flowbite's recommended UX pattern.

Trigger Types
<%# Click trigger (default) %>
<%= rui_dropdown(text: "Click me", trigger: :click) do |d| %>
  <% d.with_item(text: "Option 1", href: "#") %>
<% end %>

<%# Hover trigger %>
<%= rui_dropdown(text: "Hover over me", trigger: :hover) do |d| %>
  <% d.with_item(text: "Option 1", href: "#") %>
<% end %>

Icon-Only Triggers

Create compact dropdown triggers with just an icon - perfect for "more actions" menus, table row actions, or toolbar buttons.

Icon-Only Triggers
<%# Vertical dots (common for row actions) %>
<%= rui_dropdown(trigger_icon: "more-vertical", variant: :ghost) do |d| %>
  <% d.with_item(text: "Edit", href: "#") %>
  <% d.with_item(text: "Delete", href: "#") %>
<% end %>

<%# Horizontal dots %>
<%= rui_dropdown(trigger_icon: "more-horizontal", variant: :ghost) do |d| %>
  <% d.with_item(text: "View details", href: "#") %>
<% end %>

<%# Settings icon with color %>
<%= rui_dropdown(trigger_icon: "settings", variant: :soft, color: :primary) do |d| %>
  <% d.with_item(text: "Profile", href: "#") %>
  <% d.with_item(text: "Preferences", href: "#") %>
<% end %>

<%# Plus icon for "Add new" actions %>
<%= rui_dropdown(trigger_icon: "plus", variant: :solid, color: :success, shape: :pill) do |d| %>
  <% d.with_item(text: "New document", href: "#") %>
  <% d.with_item(text: "Upload file", href: "#") %>
<% end %>

Available Icons

The following built-in icon names are supported:

more-vertical
more-horizontal
menu
settings
plus
filter

Size Variations

Icon-only triggers scale proportionally with all sizes:

Tip: Use variant: :ghost for minimal visual footprint in tables and lists. The more-vertical icon is the most common pattern for row-level actions.

Avatar Trigger

Use an avatar image as the dropdown trigger - perfect for user account menus found in navigation headers.

Avatar Trigger
<%# Simple avatar trigger %>
<%= rui_dropdown(avatar: { src: user.avatar_url, alt: user.name }) do |d| %>
  <% d.with_item(text: "Profile", href: "#") %>
  <% d.with_item(text: "Settings", href: "#") %>
  <% d.with_divider %>
  <% d.with_item(text: "Sign out", href: "#") %>
<% end %>

<%# Avatar with name displayed %>
<%= rui_dropdown(avatar: { src: user.avatar_url, alt: user.name, name: user.name }) do |d| %>
  <% d.with_user_header(name: user.name, email: user.email, avatar: user.avatar_url, badge: "PRO") %>
  <% d.with_item(text: "Profile", href: "#") %>
  <% d.with_item(text: "Sign out", href: "#") %>
<% end %>

<%# Avatar placeholder (shows initials when no image) %>
<%= rui_dropdown(avatar: { name: user.name }) do |d| %>
  <% d.with_item(text: "Profile", href: "#") %>
<% end %>

Tip: When no src is provided, a placeholder with the user's initials is displayed. The avatar has a subtle ring effect on hover.

User Header

Display a rich user profile header inside the dropdown with avatar, name, email, and optional badge.

User Header
<%= rui_dropdown(text: "Account Menu", color: :primary) do |d| %>
  <%# Rich user header with avatar, name, email, and badge %>
  <% d.with_user_header(
    name: "Bonnie Green",
    email: "bonnie@example.com",
    avatar: "https://example.com/avatar.jpg",
    badge: "Admin"
  ) %>
  <% d.with_item(text: "Dashboard", href: "#") %>
  <% d.with_item(text: "Profile", href: "#") %>
  <% d.with_item(text: "Settings", href: "#") %>
  <% d.with_divider %>
  <% d.with_item(text: "Sign out", href: "#") %>
<% end %>

<%# User header without avatar (shows initials) %>
<% d.with_user_header(name: "John Doe", email: "john@example.com", badge: "PRO") %>

Checkbox Items

Add multi-select options with checkbox items. Clicking checkboxes toggles them without closing the dropdown.

Checkbox Items
<%= rui_dropdown(text: "Notifications", color: :primary) do |dropdown| %>
  <% dropdown.with_header(text: "Notification Settings") %>
  <% dropdown.with_checkbox_item(
    text: "Email notifications",
    name: "notifications[]",
    value: "email",
    checked: true,
    help_text: "Receive updates via email"
  ) %>
  <% dropdown.with_checkbox_item(
    text: "Push notifications",
    name: "notifications[]",
    value: "push",
    help_text: "Get push alerts on your device"
  ) %>
  <% dropdown.with_checkbox_item(
    text: "SMS notifications",
    name: "notifications[]",
    value: "sms",
    help_text: "Receive text messages"
  ) %>
  <% dropdown.with_divider %>
  <% dropdown.with_checkbox_item(
    text: "Marketing emails",
    name: "notifications[]",
    value: "marketing",
    disabled: true,
    help_text: "Promotional content and offers"
  ) %>
<% end %>

Tip: Use name="field[]" with array notation to submit multiple checkbox values as an array.

Radio Items

Add single-select options with radio items. All radios with the same name form a group. Clicking a radio selects it without closing the dropdown.

Radio Items
<%= rui_dropdown(text: "Account Type", color: :primary) do |dropdown| %>
  <% dropdown.with_header(text: "Select Account Type") %>
  <% dropdown.with_radio_item(
    text: "Individual",
    name: "account_type",
    value: "individual",
    checked: true,
    help_text: "Personal use, single user"
  ) %>
  <% dropdown.with_radio_item(
    text: "Team",
    name: "account_type",
    value: "team",
    help_text: "Up to 10 team members"
  ) %>
  <% dropdown.with_radio_item(
    text: "Enterprise",
    name: "account_type",
    value: "enterprise",
    help_text: "Unlimited members, custom features"
  ) %>
<% end %>

Note: All radio items with the same name attribute form a group where only one can be selected at a time.

Headers & Dividers

Use slots to organize items with section headers and visual dividers.

Headers & Dividers
<%= rui_dropdown(text: "User Menu") do |dropdown| %>
  <% dropdown.with_header(text: "Signed in as") %>
  <% dropdown.with_item(text: "john@example.com", disabled: true) %>
  <% dropdown.with_divider %>
  <% dropdown.with_header(text: "Account") %>
  <% dropdown.with_item(text: "Your Profile", href: "#") %>
  <% dropdown.with_item(text: "Settings", href: "#") %>
  <% dropdown.with_divider %>
  <% dropdown.with_item(text: "Sign out", href: "#") %>
<% end %>

Items with Icons

Add Lucide icons to menu items using the icon: parameter for enhanced visual recognition.

Items with Icons
<%= rui_dropdown(text: "File Actions", color: :primary) do |d| %>
  <% d.with_item(text: "New File", href: "#", icon: "file-plus") %>
  <% d.with_item(text: "Open", href: "#", icon: "folder-open") %>
  <% d.with_item(text: "Save", href: "#", icon: "save") %>
  <% d.with_divider %>
  <% d.with_item(text: "Share", href: "#", icon: "share-2") %>
  <% d.with_item(text: "Download", href: "#", icon: "download") %>
  <% d.with_divider %>
  <% d.with_item(text: "Delete", href: "#", icon: "trash-2") %>
<% end %>

Tip: Use any Lucide icon name (e.g., "user", "settings", "trash-2"). Icons automatically inherit the item's text color.

Active State for Navigation

Mark navigation items as active/current to highlight the current page. No more messy ternary operators!

Basic Active State

Active State with Default Styling
<%= rui_dropdown(text: "Navigation") do |d| %>
  <% d.with_item(
    text: "Dashboard",
    href: dashboard_path,
    icon: :home,
    active: current_page?(dashboard_path)
  ) %>

  <% d.with_item(
    text: "Settings",
    href: settings_path,
    icon: :settings,
    active: request.path.start_with?(settings_path)
  ) %>
<% end %>

Custom Active Styling

Use active_class: to customize how active items look. This only applies when the item is active.

Custom Active Styling
<%= rui_dropdown(text: "Navigation") do |d| %>
  <% d.with_item(
    text: "Dashboard",
    href: dashboard_path,
    active: current_page?(dashboard_path),
    active_class: "bg-blue-50 text-blue-900 font-bold border-l-4 border-blue-600"
  ) %>
<% end %>

✨ New in v0.35.0: Active state support eliminates the need for conditional class ternaries!

Before (messy):

class: "\#{current_page?(path) ? 'text-red-700' : 'text-gray-700'}"

After (clean):

active: current_page?(path), active_class: "text-red-700"

Key Differences:

  • active_class: - Only applies when active: true (recommended for current page styling)
  • class: - Always applies regardless of active state (use for consistent styling)
  • Active state takes precedence over danger state

Disabled & Error States

Handle disabled dropdowns and validation errors for form integration.

Disabled Dropdown

Disabled State
<%# Disabled action dropdown %>
<%= rui_dropdown(text: "Disabled", disabled: true) do |d| %>
  <% d.with_item(text: "Option 1", href: "#") %>
<% end %>

<%# Disabled selection dropdown %>
<%= rui_dropdown(
  :status,
  collection: { "Active" => "active" },
  label: "Status",
  disabled: true,
  help_text: "This field is currently locked"
) %>

Error State

Error State
<%= rui_dropdown(
  :category,
  collection: { "Technology" => "tech", "Design" => "design" },
  label: "Category",
  required: true,
  error_message: "Please select a category"
) %>

<%# With form builder (auto-detects model errors) %>
<%= form_with(model: @post) do |f| %>
  <%= f.rui_dropdown(:category_id,
    collection: Category.all,
    label: "Category",
    required: true
  ) %>
  <%# Error is auto-displayed from @post.errors[:category_id] %>
<% end %>

Collections

Multiple collection formats are supported for selection dropdowns.

Hash Collection

Hash Collection
<%= rui_dropdown(
  :status,
  collection: { "Draft" => "draft", "Published" => "published", "Archived" => "archived" },
  label: "Status"
) %>

Array of Pairs

Array of Pairs
<%= rui_dropdown(
  :priority,
  collection: [["Low", 1], ["Medium", 2], ["High", 3]],
  label: "Priority"
) %>

ActiveRecord Collection

ActiveRecord Collection
<%= rui_dropdown(
  :category_id,
  collection: Category.all,
  value_method: :id,
  text_method: :name,
  label: "Category"
) %>

Common Use Cases

Real-world patterns for using dropdowns in your application.

Navigation Menu

Use hover trigger for navigation dropdowns that appear on mouseover.

Navigation Menu
<nav class="flex items-center gap-2">
  <%= rui_dropdown(text: "Products", variant: :ghost, trigger: :hover) do |d| %>
    <% d.with_item(text: "Analytics", href: "#", icon: "bar-chart-2") %>
    <% d.with_item(text: "Automation", href: "#", icon: "zap") %>
    <% d.with_item(text: "Integration", href: "#", icon: "link") %>
  <% end %>

  <%= rui_dropdown(text: "Solutions", variant: :ghost, trigger: :hover) do |d| %>
    <% d.with_item(text: "For Startups", href: "#") %>
    <% d.with_item(text: "For Enterprise", href: "#") %>
  <% end %>
</nav>

User Account Menu

Combine avatar trigger with user header for a complete profile menu. Use danger: true for destructive actions.

User Account Menu
<%= rui_dropdown(avatar: { src: current_user.avatar_url, alt: current_user.name, name: current_user.name }) do |d| %>
  <% d.with_user_header(
    name: current_user.name,
    email: current_user.email,
    avatar: current_user.avatar_url,
    badge: current_user.role.humanize
  ) %>
  <% d.with_item(text: "Your Profile", href: profile_path, icon: "user") %>
  <% d.with_item(text: "Settings", href: "#", icon: "settings") %>
  <% d.with_divider %>
  <% d.with_item(text: "Sign out", href: logout_path, icon: "log-out", danger: true) %>
<% end %>

Table Row Actions

Use icon-only triggers for compact action menus in data tables. Delete actions use danger: true.

Name Status Actions
Project Alpha Active
Project Beta Draft
Table Row Actions
<table>
  <tbody>
    <% @projects.each do |project| %>
      <tr>
        <td><%= project.name %></td>
        <td><%= rui_badge(project.status, color: :success, size: :xs) %></td>
        <td class="text-right">
          <%= rui_dropdown(trigger_icon: "more-vertical", variant: :ghost, size: :sm, placement: :left) do |d| %>
            <% d.with_item(text: "View", href: project_path(project), icon: "eye") %>
            <% d.with_item(text: "Edit", href: edit_project_path(project), icon: "edit") %>
            <% d.with_divider %>
            <% d.with_item(text: "Delete", href: project_path(project), icon: "trash-2", danger: true) %>
          <% end %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table>

Filter & Sort Controls

Use checkboxes and radios for filter controls that stay open while selecting.

Filter Controls
<%# Multi-select filter with checkboxes %>
<%= rui_dropdown(trigger_icon: "filter", text: "Filters", variant: :outline) do |d| %>
  <% d.with_header(text: "Status") %>
  <% d.with_checkbox_item(text: "Active", name: "status[]", value: "active", checked: true) %>
  <% d.with_checkbox_item(text: "Pending", name: "status[]", value: "pending") %>
  <% d.with_checkbox_item(text: "Archived", name: "status[]", value: "archived") %>
<% end %>

<%# Single-select sort with radios %>
<%= rui_dropdown(text: "Sort by", variant: :outline) do |d| %>
  <% d.with_radio_item(text: "Newest first", name: "sort", value: "newest", checked: true) %>
  <% d.with_radio_item(text: "Oldest first", name: "sort", value: "oldest") %>
  <% d.with_radio_item(text: "Name (A-Z)", name: "sort", value: "name_asc") %>
<% end %>

Nested Menus (Submenus)

Use with_submenu to create nested menus that open on hover. Perfect for organizing related actions into groups.

Nested Menus
<%= rui_dropdown(text: "Options", color: :emerald) do |d| %>
  <% d.with_item(text: "New post", href: "#", icon: "plus") %>

  <%# Submenu with items array %>
  <% d.with_submenu(text: "Sort by", icon: "arrow-up-down", items: [
    { text: "Date", href: "#", checked: true },
    { text: "Name", href: "#" },
    { text: "Size", href: "#" }
  ]) %>

  <% d.with_submenu(text: "Filter", icon: "filter", items: [
    { text: "Draft", href: "#", checked: true },
    { text: "Published", href: "#" },
    { text: "Archived", href: "#" }
  ]) %>

  <% d.with_divider %>
  <% d.with_item(text: "Delete", href: "#", icon: "trash-2", danger: true) %>
<% end %>

Toggle Items

Use with_toggle_item to add on/off switches for settings within your dropdown.

Toggle Items
<%= rui_dropdown(text: "Settings", variant: :outline) do |d| %>
  <% d.with_toggle_item(text: "Enable notifications", checked: true) %>
  <% d.with_toggle_item(text: "Enable 2FA authentication") %>
  <% d.with_toggle_item(text: "Subscribe to newsletter", checked: true) %>
<% end %>

<%# With form name for form submission %>
<% d.with_toggle_item(text: "Dark mode", name: "settings[dark_mode]", checked: false) %>

Avatar Items

Use with_avatar_item to display user lists with avatars. Great for team selectors, user mentions, or assignee pickers.

Avatar Items
<%= rui_dropdown(text: "Select User", variant: :outline) do |d| %>
  <% d.with_avatar_item(text: "Leslie Livingston", avatar: "https://example.com/avatar1.jpg") %>
  <% d.with_avatar_item(text: "Michael Gough", avatar: "https://example.com/avatar2.jpg", selected: true) %>
  <% d.with_avatar_item(text: "Joseph Mcfall", avatar: "https://example.com/avatar3.jpg") %>
<% end %>

<%# Without avatar URL - uses initials from rui_avatar %>
<% d.with_avatar_item(text: "Jane Doe") %>

<%# With href for navigation %>
<% d.with_avatar_item(text: "View Profile", avatar: "...", href: "/users/1") %>

Search & Filter

Use with_search to add a search input that filters the dropdown items in real-time.

Search & Filter
<%= rui_dropdown(text: "Search Users", variant: :outline) do |d| %>
  <% d.with_search(placeholder: "Search...") %>
  <% d.with_avatar_item(text: "Bonnie Green", avatar: "...") %>
  <% d.with_avatar_item(text: "Leslie Livingston", avatar: "...") %>
  <% d.with_avatar_item(text: "Michael Gough", avatar: "...") %>
<% end %>

<%# Search with checkboxes %>
<%= rui_dropdown(text: "Filter", variant: :outline) do |d| %>
  <% d.with_search(placeholder: "Type to filter...") %>
  <% d.with_checkbox_item(text: "Option A", checked: true) %>
  <% d.with_checkbox_item(text: "Option B") %>
<% end %>

Form Builder Integration

Use f.rui_dropdown within Rails forms for automatic model binding.

Form Builder Usage
<%= form_with(model: @post) do |f| %>
  <%= f.rui_dropdown(:category_id,
    collection: Category.all,
    label: "Category",
    required: true
  ) %>

  <%= f.rui_dropdown(:status,
    collection: Post.statuses,
    label: "Status",
    help_text: "Choose the publication status"
  ) %>

  <%= f.rui_button("Save Post") %>
<% end %>

Replaces Rails Helpers

Rails Helper RapidRailsUI
f.select f.rui_dropdown(:method, collection: [...])
f.collection_select f.rui_dropdown(:method, collection: Model.all)

Accessibility

The Dropdown component follows WAI-ARIA best practices for menu widgets.

ARIA Attributes

  • aria-expanded indicates open/closed state
  • aria-haspopup="menu" indicates menu presence
  • aria-controls links trigger to menu
  • Menu uses role="menu" with role="menuitem"

Keyboard Navigation

  • Arrow Down/Up navigates through menu items
  • Enter or Space selects the focused item
  • Escape closes the menu
  • Home/End jumps to first/last item
  • Focus returns to trigger after selection

Semantic HTML

  • Uses <button> for the trigger element
  • Separator items use role="separator"
  • Required indicator includes aria-label="required"

Screen Reader Support

  • Menu items are announced with their text and state
  • Disabled items are announced as disabled
  • Group headers provide context for related items

JavaScript API

The Dropdown component uses the Menu controller for open/close and keyboard navigation.

Stimulus Actions

Use these actions via data-action attributes:

Action Description
menu#toggle Toggles dropdown open/closed
menu#open Opens the dropdown
menu#close Closes the dropdown
menu#navigateDown Move focus to next item (Down arrow)
menu#navigateUp Move focus to previous item (Up arrow)
menu#navigateFirst Move focus to first item (Home key)
menu#navigateLast Move focus to last item (End key)
menu#selectItem Select the current item and close
menu#clickOutside Close when clicking outside (auto-handled)
Using Stimulus Actions
<%# External trigger to open dropdown %>
<button data-action="menu#open">Open Menu</button>

<%# Close dropdown from inside %>
<%= rui_dropdown(title: "Options") do |d| %>
  <% d.with_item(text: "Cancel", data: { action: "menu#close" }) %>
<% end %>

Custom Events

Listen to these events for dropdown state changes:

Event When Detail
menu:opened Dropdown opens {}
menu:closed Dropdown closes {}
Listening to Events
<div data-controller="analytics"
     data-action="menu:opened->analytics#trackOpen
                  menu:closed->analytics#trackClose">
  <%= rui_dropdown(title: "Actions") do |d| %>
    <% d.with_item(text: "Edit") %>
    <% d.with_item(text: "Delete") %>
  <% end %>
</div>

// analytics_controller.js
trackOpen(event) {
  console.log("Dropdown opened")
}

Programmatic Control

Control the dropdown from JavaScript:

Programmatic Control
// Get the menu controller
const dropdownElement = document.querySelector('[data-controller="menu"]')
const menuController = application.getControllerForElementAndIdentifier(
  dropdownElement,
  "menu"
)

// Open/close/toggle
menuController.open()
menuController.close()
menuController.toggle()

// Check open state
console.log("Is open:", menuController.openValue)

API Reference

rui_dropdown

Versatile menu system for navigation, selection, or actions with slots for items, headers, dividers, and more

Parameter Type Default Description
method Symbol — Attribute name for form binding (positional first arg)
text String — Button text (action dropdowns)
collection Hash/Array/AR — Options for selection dropdown
label String — Label text above dropdown
help_text String — Help text below dropdown

Appearance

Visual styling options

Parameter Type Default Description
variant Symbol :solid Button style
:solid :outline :ghost :soft
color Symbol :zinc Color scheme - semantic or Tailwind
size Symbol :base Button size
:xs :sm :base :lg :xl
shape Symbol :rounded Border radius
:square :rounded :pill
width Symbol :base Menu width
:sm :base :lg :xl :full

Behavior

Interaction options

Parameter Type Default Description
placement Symbol :bottom Menu position
:top :right :bottom :left
trigger Symbol :click Open trigger
:click :hover

Trigger Types

Different trigger button styles

Parameter Type Default Description
icon_only Boolean false Render as icon-only button (no text, no chevron)
trigger_icon Symbol/String — Icon for icon-only trigger (more-vertical, menu, settings, plus, filter)
avatar Hash — Avatar trigger: { src:, alt:, name: }

Selection Mode

Options for selection dropdown behavior

Parameter Type Default Description
value String — Pre-selected value
prompt_text String "Select an option" Placeholder text when nothing selected
value_method Symbol :id Method to get value from AR objects
text_method Symbol :name Method to get display text from AR objects

States

State options

Parameter Type Default Description
disabled Boolean false Disable the dropdown
required Boolean false Show required indicator
error_message String — Error message to display

Item Parameters (with_item)

Parameters for individual dropdown items

Parameter Type Default Description
text String — Item text (required)
href String — Link URL (renders as <a> if present, <button> otherwise)
value String — Value for selection mode
icon Symbol — Icon name to display before text
disabled Boolean false Disable the item
danger Boolean false Apply destructive/warning styling (red)
active Boolean false Mark as current/active page (new in v0.35.0)
active_class String — Custom classes applied ONLY when active is true (new in v0.35.0)
class String — Custom classes applied to ALL states
data Hash — Custom data attributes

Form Builder

Options for f.rui_dropdown

Parameter Type Default Description
form FormBuilder — Rails form builder instance
id String auto-generated Custom ID for the component
name String — Name attribute for hidden input (selection mode)

Slots

Content slots for customizing component parts

Slot Description
with_item Add a menu item - text: (req), value:, href:, disabled:, icon:, danger:, active:, active_class:, class:, data:
with_header Add a non-interactive section header - text: (req)
with_divider Add a horizontal visual separator - (no params)
with_icon Add a leading icon to trigger button - name (req)
with_checkbox_item Add a checkbox item - text: (req), name:, value:, checked:, disabled:, help_text:
with_radio_item Add a radio item - text: (req), name:, value:, checked:, disabled:, help_text:
with_user_header Add user profile header - name: (req), email:, avatar:, badge:
with_submenu Add nested submenu - text: (req), icon:, items: (req), disabled:
with_toggle_item Add toggle switch item - text: (req), name:, checked:, disabled:
with_avatar_item Add avatar item - text: (req), avatar:, description:, href:, disabled:
with_search Add search input at top - placeholder:, name:
with_footer Add footer with action buttons - text: (req), href:, color:

JavaScript & Namespacing

Dropdown ships a Stimulus controller registered under the identifier menu. If you set config.stimulus_namespace = "rui" ( see guide ), every emitted Stimulus attribute is prefixed: the identifier becomes rui--menu.

💡 Working with your own Stimulus controllers? Read Stimulus Namespace to keep the gem's controllers from colliding with yours.

JS registration (app/javascript/controllers/index.js)

Without namespace
import MenuController from "./rapid_rails_ui/menu_controller"
application.register("menu", MenuController)
With config.stimulus_namespace = "rui"
import MenuController from "./rapid_rails_ui/menu_controller"
application.register("rui--menu", MenuController)

Custom trigger button

Without namespace
<button data-action="menu#open">Trigger</button>
With namespace (either form works)
<button data-action="rui--menu#open">Trigger</button>

<button data-action="<%= stimulus_action("<%= controller_id %>", "click->#open") %>">Trigger</button>

Related Components