Menu

Accessible dropdown action menus following the WAI-ARIA Menu Button pattern, featuring roving focus keyboard navigation, shortcut keys, danger states, and focus return.

Interactive Preview

Interactive Preview

Interactive Preview

API reference

MenuProps

triggerReact.ReactNode—

Name: trigger

Description: The trigger element (e.g. <Button>). Automatically wired with accessibility attributes.

Type: React.ReactNode

Default: —

childrenReact.ReactNode—

Name: children

Description: Menu content containing MenuItem, MenuDivider, and MenuGroup.

Type: React.ReactNode

Default: —

labelstring—

Name: label

Description: Accessible label for the menu if trigger does not contain visible text.

Type: string

Default: —

isOpenboolean—

Name: isOpen

Description: Controlled open state.

Type: boolean

Default: —

defaultOpenbooleanfalse

Name: defaultOpen

Description: Initial open state when uncontrolled.

Type: boolean

Default: false

onOpenChange(isOpen: boolean) => void—

Name: onOpenChange

Description: Callback fired when open state changes.

Type: (isOpen: boolean) => void

Default: —

placement'bottom-start' | 'bottom-end' | 'top-start' | 'top-end''bottom-start'

Name: placement

Description: Alignment of dropdown menu relative to the trigger.

Type: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'

Default: 'bottom-start'

closeOnSelectbooleantrue

Name: closeOnSelect

Description: Whether selecting a menu item automatically closes the menu.

Type: boolean

Default: true

MenuItemProps

childrenReact.ReactNode—

Name: children

Description: Item label text or elements.

Type: React.ReactNode

Default: —

onClick(event: React.MouseEvent | React.KeyboardEvent) => void—

Name: onClick

Description: Action handler invoked on click, Enter, or Space.

Type: (event: React.MouseEvent | React.KeyboardEvent) => void

Default: —

hrefstring—

Name: href

Description: Optional destination URL to render as an accessible navigation link.

Type: string

Default: —

iconReact.ReactNode—

Name: icon

Description: Optional leading icon or SVG element.

Type: React.ReactNode

Default: —

shortcutstring—

Name: shortcut

Description: Optional keyboard shortcut label (e.g. ⌘S).

Type: string

Default: —

disabledbooleanfalse

Name: disabled

Description: Disables interaction and skips the item during keyboard navigation.

Type: boolean

Default: false

dangerbooleanfalse

Name: danger

Description: Applies destructive danger styling for critical actions (e.g. Delete).

Type: boolean

Default: false

MenuGroupProps

labelstring—

Name: label

Description: Optional section header text associated via aria-labelledby.

Type: string

Default: —

childrenReact.ReactNode—

Name: children

Description: Group content items.

Type: React.ReactNode

Default: —

KeyFocus TargetBehavior
Enter / Space / ↓Trigger ButtonOpens menu and moves focus to the first non-disabled item.
↑Trigger ButtonOpens menu and moves focus to the last non-disabled item.
↓Menu ItemMoves focus to the next item, skipping disabled items and wrapping around.
↑Menu ItemMoves focus to the previous item, skipping disabled items and wrapping around.
HomeMenu ItemJumps focus to the first non-disabled item.
EndMenu ItemJumps focus to the last non-disabled item.
EscapeMenu ItemCloses menu and immediately returns focus to the trigger button.
TabMenu ItemCloses menu and allows normal tab order progression.
  • Trigger Semantics: Configured with aria-haspopup="menu" and aria-expanded.
  • Roving Tabindex: Only the active item receives focus during arrow key navigation; pressing Tab doesn't trap keyboard focus inside the menu.
  • Focus Return (WCAG 2.4.3): Dismissing via Escape or activating an action item returns focus directly to the trigger button.
  • Semantic Groups & Dividers: Uses role="group" with accessible section headers and role="separator" for dividing lines.
  • Zero 3rd-Party Dependencies: Native React and CSS implementation with zero external runtime footprint.
Open Menu stories in Storybook