DropdownMenu

A list of actions or toggles opened from a button.

StableSource

Last action: nothing yet

Import

tsx
import { DropdownMenu } from "@merid/react";

Anatomy

tsx
<DropdownMenu.Root>
  <DropdownMenu.Trigger />
  <DropdownMenu.Content>
    <DropdownMenu.Label />
    <DropdownMenu.Group>
      <DropdownMenu.Item />
      <DropdownMenu.CheckboxItem />
    </DropdownMenu.Group>
    <DropdownMenu.Separator />
  </DropdownMenu.Content>
</DropdownMenu.Root>
  • Root holds the open state. It renders no element.
  • Trigger is a <button> with aria-haspopup="menu".
  • Content is the portalled role="menu" panel, positioned against the trigger. It only mounts while open.
  • Item runs an action and closes the menu. CheckboxItem toggles a setting and keeps the menu open.
  • Label, Group and Separator organise longer menus.

Examples

Checkbox items

CheckboxItem works controlled (checked + onCheckedChange) or uncontrolled (defaultChecked). Choosing one does not close the menu.

Placement

placement accepts any Floating UI placement ("top", "bottom-end", "right-start"…). The menu flips and shifts to stay in the viewport.

Keeping the menu open

onSelect receives a cancelable event. Call event.preventDefault() to keep the menu open after an item is chosen.

tsx
<DropdownMenu.Item onSelect={(event) => { event.preventDefault(); refresh(); }}>
  Refresh
</DropdownMenu.Item>

API reference

Root

PropTypeDefaultDescription
openboolean–Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void–Called when the open state should change.
childrenReactNode–Trigger and Content.

Trigger

Accepts every <button> attribute. type defaults to "button" (not set when asChild is used).

PropTypeDefaultDescription
asChildbooleanfalseRender the single child element (e.g. your own `Button`) instead of a `<button>`, merging props, ref and handlers.
refRef<HTMLButtonElement>–Forwarded ref to the button.

Content

Accepts every <div> attribute.

PropTypeDefaultDescription
placementPlacement"bottom-start"Preferred placement relative to the trigger.
sideOffsetnumber6Distance from the trigger in px.
containerElement | nulldocument.bodyPortal target. `undefined` uses `document.body`; `null` renders nothing until the target exists.
refRef<HTMLDivElement>–Forwarded ref to the menu element.

Item

Accepts every <div> attribute except onSelect. Your onClick, onKeyDown and onPointerMove run first; call event.preventDefault() in them to skip the built-in behaviour (activation, highlighting).

PropTypeDefaultDescription
onSelect(event: Event) => void–Called when the item is chosen by click, Enter or Space. Call preventDefault to keep the menu open.
disabledbooleanfalseDisable the item; it is skipped by arrow keys.
textValuestring–Text used for typeahead when children are not plain text.
leadingReactNode–Icon or element shown before the label.
trailingReactNode–Shortcut hint or element shown after the label.
refRef<HTMLDivElement>–Forwarded ref to the item element.

CheckboxItem

Accepts every <div> attribute except onSelect, with the same handler composition as Item. leading is not rendered; the check mark takes its place.

PropTypeDefaultDescription
checkedboolean–Controlled checked state.
defaultCheckedbooleanfalseInitial checked state when uncontrolled.
onCheckedChange(checked: boolean) => void–Called with the new checked state. The menu stays open.
disabledbooleanfalseDisable the item; it is skipped by arrow keys.
textValuestring–Text used for typeahead when children are not plain text.
trailingReactNode–Shortcut hint or element shown after the label.
refRef<HTMLDivElement>–Forwarded ref to the item element.

Group, Label, Separator

All three accept every <div> attribute. Group renders role="group"; give it an aria-label. Label is presentational text; Separator renders role="separator".

Styling

ClassElement
.mrd-menuContent panel
.mrd-menu__itemItem and CheckboxItem
.mrd-menu__leading, .mrd-menu__trailingLeading icon, trailing hint
.mrd-menu__label-textItem text
.mrd-menu__checkCheckboxItem check slot
.mrd-menu__labelLabel
.mrd-menu__separatorSeparator
AttributeOnValues
data-stateTrigger (or the asChild element)open, closed
data-stateContentopen
data-stateCheckboxItemchecked, unchecked
data-disabledItem, CheckboxItempresent when disabled

Accessibility

Follows the WAI-ARIA Menu Button pattern. The trigger carries aria-haspopup="menu", aria-expanded and aria-controls; the menu is labelled by the trigger. Focus moves into the menu on open and returns to the trigger on Escape or selection.

KeyAction
EnterOn the trigger: open and focus the first item. On an item: activate it.
SpaceOn the trigger: open and focus the first item. On an item: activate it.
ArrowDownOn the trigger: open on the first item. In the menu: next item, wrapping.
ArrowUpOn the trigger: open on the last item. In the menu: previous item, wrapping.
HomeFirst item.
EndLast item.
A–ZTypeahead to the next item starting with the typed text.
EscapeClose and return focus to the trigger.
TabClose the menu and move focus on.

Guidelines

Do

Use a menu for secondary actions on an object — rename, duplicate, archive — and keep labels to a verb or short phrase.

Avoid

Use a menu to pick a form value. Choosing one value from a list is a Select.

Do

Put destructive actions last, after a separator.

Avoid

Hide the only way to reach a primary action inside a menu.

  • Select — choose one value for a form.
  • Popover — non-modal panel with arbitrary content.
  • Button — the trigger's usual look.