Dropdown Menu

A headless, accessible dropdown menu with submenus, checkbox and radio items, typeahead, and full keyboard navigation.

Features

  • Menu button pattern
  • Checkbox and radio items
  • Submenu support
  • Active descendant tracking
  • Typeahead
  • Headless styling

Installation

npm install @ariaui/dropdown-menu

Examples

Four playgrounds migrated from the legacy doc: a full account-style menu, a submenu-only flow, selectionMode="multiple" with CheckboxItem, and a RadioGroup block. Each has its own snippet; styling uses semantic tokens (border-border, bg-background, etc.).

Keyboard support includes roving focus, typeahead, arrows in submenus, and Escape to dismiss—see the keyboard table below.

Full menu

Account header, shortcuts, submenu, checkbox and radio groups, and a destructive-style action.

Preview

No preview output yet.

live.tsxReady

With submenu

Nested menu using Sub, SubTrigger, and SubContent.

Preview

No preview output yet.

live.tsxReady

With checkboxes

Multiple selection with CheckboxItem rows for independent toggles.

Preview

No preview output yet.

live.tsxReady

With radio group

RadioGroup coordinates mutually exclusive RadioItem values.

Preview

No preview output yet.

live.tsxReady

Framer Motion

Animated root and submenu content using asChild composition with Framer Motion.

Preview

No preview output yet.

live.tsxReady

Anatomy

tsx
import * as DropdownMenu from "@ariaui/dropdown-menu";

export default function Example() {
  return (
    <DropdownMenu.Root>
      <DropdownMenu.Trigger />
      <DropdownMenu.Content>
        <DropdownMenu.Group>
          <DropdownMenu.Label />
          <DropdownMenu.Item />
        </DropdownMenu.Group>
        <DropdownMenu.Separator />
        <DropdownMenu.Sub>
          <DropdownMenu.SubTrigger />
          <DropdownMenu.SubContent>
            <DropdownMenu.Item />
          </DropdownMenu.SubContent>
        </DropdownMenu.Sub>
        <DropdownMenu.CheckboxItem />
        <DropdownMenu.RadioGroup>
          <DropdownMenu.RadioItem />
        </DropdownMenu.RadioGroup>
      </DropdownMenu.Content>
    </DropdownMenu.Root>
  );
}

API Reference

Root

State container. Owns the open state, selection mode, and provides context to all child components.

PropTypeDefault
onValueChange
(value: string) => void
open
boolean
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void
selectionMode
"single" | "multiple""single"
offset
{ x: number; y: number }

Trigger

Button that toggles the dropdown menu. Focus returns here when the menu closes.

AttributeValues
aria-haspopupmenu
aria-expanded"true" when open, "false" when closed
aria-controlsID of the Content element

Content

Floating menu container. Manages item registration, active-item tracking via aria-activedescendant, and keyboard navigation.

PropTypeDefault
asChild
booleanfalse
AttributeValues
rolemenu
aria-labelledbyID of the Trigger element
aria-activedescendantID of the currently active item

Item

A selectable menu item. Activates on click, Enter, or Space.

PropTypeDefault
value
string
disabled
booleanfalse
AttributeValues
rolemenuitem
[data-active]Present when keyboard-highlighted
[data-disabled]Present when disabled

CheckboxItem

A menu item with checkbox semantics. Toggles checked state on activation.

PropTypeDefault
checked
boolean
defaultChecked
booleanfalse
onCheckedChange
(checked: boolean) => void
disabled
booleanfalse
AttributeValues
rolemenuitemcheckbox
aria-checked"true" or "false"
[data-active]Present when keyboard-highlighted
[data-disabled]Present when disabled

RadioGroup

Groups related RadioItem components and manages the selected value.

PropTypeDefault
value
string
defaultValue
string
onValueChange
(value: string) => void

RadioItem

A menu item with radio semantics. Selects its value within the parent RadioGroup on activation.

PropTypeDefault
value*
string
disabled
booleanfalse
AttributeValues
rolemenuitemradio
aria-checked"true" when selected
[data-active]Present when keyboard-highlighted
[data-disabled]Present when disabled

Sub

State container for a submenu. Wraps SubTrigger and SubContent.

PropTypeDefault
open
boolean
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void
offset
{ x: number; y: number }

SubTrigger

A menu item that opens a nested submenu on hover or keyboard.

AttributeValues
rolemenuitem
aria-haspopupmenu
aria-expanded"true" when submenu is open
aria-controlsID of the SubContent element
[data-active]Present when keyboard-highlighted

SubContent

Floating container for submenu items. Positioned relative to SubTrigger.

PropTypeDefault
asChild
booleanfalse
AttributeValues
rolemenu
aria-activedescendantID of the currently active submenu item

Group

Groups related menu items.

Label

Non-interactive label for a group of items.

Separator

Visual divider between menu sections.

AttributeValues
roleseparator

Keyboard

ShortcutAction
Open the menu (from trigger) or move to the next item. Wraps to first.
Open the menu targeting last item (from trigger) or move to the previous item. Wraps to last.
HomeMove to the first enabled item.
EndMove to the last enabled item.
Enter/SpaceOpen the menu (from trigger) or activate the focused item.
EscClose the current menu layer and return focus to its trigger.
Open a submenu when a SubTrigger is focused (LTR).
Close a submenu and return to the parent menu (LTR).

Accessibility

The Dropdown Menu component implements the WAI-ARIA Menu Button pattern using the aria-activedescendant model:

  • Trigger exposes aria-haspopup="menu", aria-expanded, and aria-controls pointing to the Content.
  • Content renders as role="menu" with aria-labelledby referencing the Trigger and aria-activedescendant tracking the active item.
  • Item renders as role="menuitem" with data-active for the focused state.
  • CheckboxItem renders as role="menuitemcheckbox" with aria-checked.
  • RadioItem renders as role="menuitemradio" with aria-checked.
  • SubTrigger carries aria-haspopup="menu" and aria-expanded to announce submenu availability.
  • DOM focus stays on the menu container during keyboard navigation; active items are communicated via aria-activedescendant.
  • Navigation is cyclic, disabled items are skipped, and typeahead prefix matching is supported.
Previous
Drawer
Next
Form