@akhatmirzo/ui

DropdownMenu

An actions menu with icons, keyboard shortcut labels, checkable items and submenus.

Import

import { DropdownMenu } from '@akhatmirzo/ui';

When to use

  • Actions on a single object — the "⋯" button in a table row, a card menu, a profile menu.
  • View options — CheckboxItem (which columns to show) and RadioGroup (sort order).
  • Not for picking a form value. For form fields use Select or Combobox — they hold the value and submit it with the form.
  • Destructive actions (variant="danger") go last, after a separator; confirm them with confirm() when needed.
  • Keep submenus one level deep — deeper nesting is awkward with a keyboard and on phones.

Anatomy

PartWhat it does
DropdownMenuState: open, defaultOpen, onOpenChange
DropdownMenu.TriggerOpens the menu. Without asChild, it looks like an outline button
DropdownMenu.ContentThe panel: portal, positioning, looping navigation
DropdownMenu.ItemAn action: icon, shortcut, variant, onSelect
DropdownMenu.CheckboxItemA toggleable item (✓)
DropdownMenu.RadioGroup / RadioItemOne choice out of several (●)
DropdownMenu.LabelA group heading
DropdownMenu.GroupGroups items logically
DropdownMenu.SeparatorA divider line
DropdownMenu.Sub / SubTrigger / SubContentA submenu

Checkable items

CheckboxItem uses checked / onCheckedChange; RadioGroup uses value / onValueChange (or an uncontrolled defaultValue). The menu closes after a pick; to let users toggle several items in a row, call e.preventDefault() inside onSelect.

Saralash: new

Put a SubTrigger and a SubContent inside Sub. The submenu opens on hover, or with → on the keyboard.

Label and Group split the menu into sections. Give an Item the asChild prop to render an <a> (or a Next.js Link) as the item — the icon and the shortcut label are placed inside the link. align="end" lines the panel up with the trigger's right edge.

<DropdownMenu.Item asChild icon={<Settings />}>
  <Link href="/settings">Settings</Link>
</DropdownMenu.Item>

shortcut is only a label

shortcut={['mod', 'E']} shows ⌘E in the menu (Ctrl E on Windows) and sets aria-keyshortcuts, but it does not listen for the key press. Wire up the shortcut itself with useHotkeys.

States

A disabled item can't be clicked and is skipped by keyboard navigation. The trigger can be disabled too. Use open + onOpenChange to open the menu from code.

Props

Accepts the props of the Radix DropdownMenu Root: open, defaultOpen, onOpenChange, modal (defaults to true) and dir.

Also accepts the rest of the Radix Content props (alignOffset, onCloseAutoFocus, onEscapeKeyDown ...) and <div> attributes.

PropTypeDefaultDescription
side'top' | 'right' | 'bottom' | 'left''bottom'Preferred side
align'start' | 'center' | 'end''start'Alignment along the trigger
sideOffsetnumber6Gap to the trigger, px
collisionPaddingnumber8Minimum distance from the screen edge, px
loopbooleantrue↑ ↓ wrap around from the last item to the first
containerHTMLElement | null—Portal container (defaults to document.body)
PropTypeDefaultDescription
iconReactNode—Leading icon (16px)
shortcutstring[]—A Kbd label on the right, e.g. ['mod', 'E']
variant'default' | 'danger''default'danger — red text and highlight
onSelect(event: Event) => void—Called on select. preventDefault() keeps the menu open
disabledboolean—Disables the item
textValuestring—Text for typeahead when children aren't plain text
asChildbooleanfalseRenders the child (e.g. an <a>) as the item

CheckboxItem, RadioGroup, RadioItem

PropTypeDefaultDescription
checkedboolean | 'indeterminate'—CheckboxItem: whether it is checked
onCheckedChange(checked: boolean) => void—CheckboxItem: called on change
valuestring—RadioGroup: the selected value; RadioItem: the item's value
defaultValuestring—RadioGroup: initial value
onValueChange(value: string) => void—RadioGroup: called on change
shortcutstring[]—CheckboxItem and RadioItem: a Kbd on the right
disabledboolean—CheckboxItem and RadioItem: disables it

Sub, SubTrigger, SubContent

Sub takes open, defaultOpen and onOpenChange. SubTrigger takes icon and disabled. SubContent takes sideOffset (default 8), alignOffset (-6), collisionPadding (8), loop (true) and container.

Data attributes

AttributeOnValue
data-statetrigger, panelopen · closed
data-variantitemdefault · danger
data-highlighteditempresent under the keyboard or mouse
data-disableditempresent when disabled
data-stateCheckbox/RadioItemchecked · unchecked
data-sidepaneltop · right · bottom · left

Keyboard

KeyAction
Enter / Space / ↓Opens the menu from the trigger, focusing the first item
↓ / ↑Moves between items (wraps around)
Home / EndFirst / last item
A letterJumps to the next item starting with that letter
→ / ←Opens / closes a submenu
Enter / SpaceSelects and closes
EscCloses; focus returns to the trigger

Accessibility

  • Roles. The panel is role="menu"; items are menuitem, menuitemcheckbox or menuitemradio; the trigger gets aria-haspopup="menu" and aria-expanded. It is built on Radix DropdownMenu.
  • Shortcuts. With shortcut, the item gets aria-keyshortcuts (mod becomes Meta on macOS and Control elsewhere). The visible Kbd is hidden from screen readers.
  • Icons are aria-hidden.
  • Icon triggers. When the trigger is an IconButton, give it an aria-label ("More actions").
  • Focus. Opening moves focus into the menu; closing returns it to the trigger.

On this page