Morphos
Overlays

Dropdown

A contextual menu that appears anchored to a trigger, offering a list of selectable actions.

Interactive example

Open in Storybook

Installation

npm install @morphos/overlays
pnpm add @morphos/overlays
yarn add @morphos/overlays
bun add @morphos/overlays

Import

import { Dropdown, DropdownTrigger, DropdownMenu, DropdownItem } from '@morphos/overlays'

Usage

@Component()
class MyComponent extends StatefulComponent {
  @State() dropdown = new Dropdown()

  render() {
    return (
      <>
        <DropdownTrigger dropdown={this.dropdown}>
          Actions
        </DropdownTrigger>
        <DropdownMenu dropdown={this.dropdown}>
          <DropdownItem dropdown={this.dropdown} label="Edit" value="edit" onSelect={() => console.log('edit')} />
          <DropdownItem dropdown={this.dropdown} label="Delete" value="delete" onSelect={() => console.log('delete')} />
          <DropdownItem dropdown={this.dropdown} label="Archive" value="archive" disabled />
        </DropdownMenu>
      </>
    )
  }
}

Dropdown, DropdownTrigger, DropdownMenu, and DropdownItem are also re-exported under the aliases Menu, MenuTrigger, MenuContent, and MenuItem — they are the exact same classes, just under more familiar names. There is no separate Menu implementation.

import { Menu, MenuTrigger, MenuContent, MenuItem } from '@morphos/overlays'

Compound components

ComponentDescription
DropdownRoot state manager. Owns open state, the active (keyboard-focused) item index, and the computed anchor position.
DropdownTrigger<button> that toggles the menu on click. Sets aria-haspopup="menu", aria-expanded, aria-controls, and data-open.
DropdownMenu<ul role="menu"> rendered into a Portal, positioned with position: fixed. Handles arrow-key navigation.
DropdownItem<li role="menuitem">. Selectable via click or Enter/Space. Registers itself with the Dropdown instance for keyboard navigation.

Props — Dropdown

PropTypeDefaultDescription
openboolean—Controlled open state. When set, the consumer owns the state.
defaultOpenbooleanfalseInitial open state in uncontrolled mode.
onOpenChange(open: boolean) => void—Called when the open state changes.
closeOnSelectbooleantrueWhether selecting an item closes the menu.
side"top" | "bottom" | "left" | "right""bottom"Which edge of the trigger the menu is placed against.
align"start" | "center" | "end""start"Alignment of the menu along the trigger's perpendicular axis.
sideOffsetnumber4Gap in pixels between the trigger and the menu.
childrenChildren—Content — typically DropdownTrigger and DropdownMenu.

Methods

MethodDescription
toggle()Opens the menu if closed, closes it if open.
openDropdown()Opens the menu, sets the active index to 0, and recomputes position. Emits onOpenChange(true).
closeDropdown()Closes the menu and resets the active index. Emits onOpenChange(false).
handleKeyDown(event)Handles ArrowDown / ArrowUp / Home / End / Escape / Tab — called internally by DropdownMenu's onKeyDown.

Props — DropdownTrigger

PropTypeDefaultDescription
dropdownDropdown—The Dropdown instance this trigger controls.
childrenChildren—The trigger element content.
classstring—CSS class.
idstring—HTML id. Falls back to an auto-generated id.
aria-labelstring—Accessible label for the trigger.

Props — DropdownMenu

PropTypeDefaultDescription
dropdownDropdown—The Dropdown instance this menu belongs to.
childrenChildren—DropdownItem elements.
classstring—CSS class.
idstring—HTML id. Falls back to an auto-generated id.
aria-labelstring—Accessible label for the menu.
aria-labelledbystring—ID of the element that labels the menu. Falls back to the trigger's id.

Props — DropdownItem

PropTypeDefaultDescription
dropdownDropdown—The Dropdown instance this item belongs to.
valuestring—Unique value identifier.
labelstring—Display text, used when no children are passed.
disabledbooleanfalseDisables the item.
onSelect() => void—Called when the item is selected.
childrenChildren—Item content. Falls back to label if omitted.
classstring—CSS class.
idstring—HTML id.

Keyboard navigation

KeyAction
ArrowDownMove the active index to the next item (wraps around)
ArrowUpMove the active index to the previous item (wraps around)
HomeMove the active index to the first item
EndMove the active index to the last item
Enter / SpaceSelect the focused item
EscapeClose the menu
TabClose the menu

data-* attributes

AttributeElementWhen present
data-openDropdownTriggerMenu is open
data-openDropdownMenu rootMenu is open
data-disabledDropdownItemdisabled is true

DropdownMenu renders with role="menu" and each DropdownItem renders with role="menuitem" inside a Portal. The menu's position is computed with computeAnchorPosition() from @morphos/core, using DropdownTrigger's bounding rect and the side/align/sideOffset props on Dropdown. Dropdown does not trap focus or lock scroll.

Styling example

.morphos-dropdown-menu {
  z-index: 100;
  min-width: 10rem;
  padding: var(--morphos-space-1);
  list-style: none;
  border: 1px solid var(--morphos-color-border);
  border-radius: var(--morphos-radius-md);
}

.morphos-dropdown-item {
  display: flex;
  justify-content: space-between;
  padding: var(--morphos-space-2) var(--morphos-space-3);
  border-radius: var(--morphos-radius-sm);
  cursor: pointer;
}

.morphos-dropdown-item[data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

On this page