MenusReact
A list of actions in a dropdown, opened by a button and navigated with the keyboard.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item />
<Menu.CheckboxItem />
<Menu.SubmenuRoot>
<Menu.SubmenuTrigger />
</Menu.SubmenuRoot>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
// Not in this example: Menu.Arrow, Menu.Backdrop, Menu.CheckboxItemIndicator, Menu.Clear, Menu.Empty, Menu.FilterProvider, Menu.Group, Menu.GroupLabel, Menu.Input, Menu.LinkItem, Menu.List, Menu.RadioGroup, Menu.RadioItem, Menu.RadioItemIndicator, Menu.Separator, Menu.ViewportExamples
Open On Hover
openOnHover and delay open the menu when the pointer rests on the trigger.
Checkbox Items
CheckboxItem toggles a setting on or off.
Radio Items
RadioGroup and RadioItem pick one option of a set.
Group Labels
GroupLabel names a Group or a RadioGroup.
Submenu
SubmenuRoot and SubmenuTrigger open a nested menu.
Detached Triggers Simple
A trigger outside Root opens it through a handle.
Detached Triggers Controlled
open with triggerId lets several triggers share one menu.
Arrow
Arrow points the popup at its trigger.
Detached Triggers Full
Viewport animates the content when another trigger opens the same menu.
Filterable Menu
FilterProvider, Input and List narrow the items as the person types.
Filter
A menu of grouped actions with a filter field, and submenus with their own. Groups with no match hide, and the empty state shows when nothing matches.
API reference
99 props across 25 parts, in React syntax. className, style, render are listed once, under Shared props.
ArrowMenu.Arrow
Displays an element positioned against the menu anchor. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the menu popup is open. |
data-closed | Present when the menu popup is closed. |
data-side | Indicates which side the popup is positioned relative to the anchor. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-uncentered | Present when the menu arrow is uncentered. |
BackdropMenu.Backdrop
An overlay displayed beneath the menu popup. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the menu is open. |
data-closed | Present when the menu is closed. |
data-starting-style | Present when the menu begins animating in. |
data-ending-style | Present when the menu is animating out. |
CheckboxItemMenu.CheckboxItem
A menu item that toggles a setting on or off. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
labelOverrides the text used for keyboard text navigation and filtering. | string | — |
defaultCheckedWhether the checkbox item is initially ticked. | boolean | false |
checkedWhether the checkbox item is currently ticked. | boolean | — |
onCheckedChangeEvent handler called when the checkbox item is ticked or unticked. | ((checked: boolean, eventDetails: MenuRootChangeEventDetails) => void) | — |
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | false |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
disabledWhether the component should ignore user interaction. | boolean | false |
| Data attribute | Description |
|---|---|
data-checked | Present when the menu checkbox item is checked. |
data-unchecked | Present when the menu checkbox item is not checked. |
data-disabled | Present when the menu checkbox item is disabled. |
data-highlighted | Present when the menu checkbox item is highlighted. |
CheckboxItemIndicatorMenu.CheckboxItemIndicator
Indicates whether the checkbox item is ticked. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the HTML element in the DOM when the checkbox item is not checked. | boolean | false |
| Data attribute | Description |
|---|---|
data-checked | Present when the menu checkbox item is checked. |
data-unchecked | Present when the menu checkbox item is not checked. |
data-disabled | Present when the menu checkbox item is disabled. |
data-starting-style | Present when the indicator begins animating in. |
data-ending-style | Present when the indicator is animating out. |
ClearMenu.Clear
A button that clears the input text. Requires the menu to be wrapped in Menu.FilterProvider. when the input has text. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
disabledWhether the component should ignore user interaction. | boolean | false |
| Data attribute | Description |
|---|---|
data-disabled | Present when the button is disabled. |
EmptyMenu.Empty
A message shown when the menu has no matching items. Requires the menu to be wrapped in Menu.FilterProvider. Renders a <div>.
FilterProviderMenu.FilterProvider
Enables filtering for the menu or submenu it wraps. Add Menu.Input to the popup and place its items in Menu.List. Wrap each searchable submenu in its own provider. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
defaultValueThe uncontrolled filter query when the menu is initially rendered. | string | — |
valueThe filter query. | string | — |
onValueChangeEvent handler called when the filter query changes. | ((value: string, eventDetails: FilterDropdownRootChangeEventDetails) => void) | — |
autoHighlightWhether filtering highlights the first matching item automatically. | boolean | "always" | false |
filterFilter function used to match items against the query. | ((text: string, query: string) => boolean) | null | — |
localeThe locale the default filter uses for string comparison. | Intl.LocalesArgument | — |
childrenThe <Menu.Root> or <Menu.SubmenuRoot> to make filterable. | React.ReactNode | — |
GroupMenu.Group
Groups related menu items with the corresponding label. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
childrenThe content of the component. | React.ReactNode | — |
GroupLabelMenu.GroupLabel
An accessible label that is automatically associated with its parent group. Renders a <div>.
InputMenu.Input
A search field that filters the menu items. Requires the menu to be wrapped in Menu.FilterProvider. Renders an <input>.
| Data attribute | Description |
|---|---|
data-highlighted | Present while the input shows its focus ring. Cleared when keyboard navigation highlights an item. |
ItemMenu.Item
An individual interactive item in the menu. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
labelOverrides the text used for keyboard text navigation and filtering. | string | — |
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | true |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
disabledWhether the component should ignore user interaction. | boolean | false |
| Data attribute | Description |
|---|---|
data-highlighted | Present when the menu item is highlighted. |
data-disabled | Present when the menu item is disabled. |
LinkItemMenu.LinkItem
A link in the menu that can be used to navigate to a different page or section. Renders an <a>.
| Prop | Type | Default |
|---|---|---|
labelOverrides the text used for keyboard text navigation and filtering. | string | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | false |
| Data attribute | Description |
|---|---|
data-highlighted | Present when the link is highlighted. |
ListMenu.List
Groups menu items so other content, such as a filter input, can share the popup. Renders a <div>.
PopupMenu.Popup
A container for the menu items. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
finalFocusDetermines the element to focus when the menu is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
children | React.ReactNode | — |
| Data attribute | Description |
|---|---|
data-open | Present when the menu is open. |
data-closed | Present when the menu is closed. |
data-starting-style | Present when the menu begins animating in. |
data-ending-style | Present when the menu is animating out. |
data-side | Indicates which side the popup is positioned relative to the anchor. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-instant | Present if animations should be instant. |
PortalMenu.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
PositionerMenu.Positioner
Positions the menu popup against the trigger. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
alignHow to align the popup relative to the specified side. | Align | 'center' |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
| Data attribute | Description |
|---|---|
data-open | Present when the menu popup is open. |
data-closed | Present when the menu popup is closed. |
data-anchor-hidden | Present when the anchor is hidden. |
data-side | Indicates which side the popup is positioned relative to the anchor. |
data-align | Indicates how the popup is aligned relative to the specified side. |
| CSS variable | Description |
|---|---|
--available-width | The available width between the anchor and the edge of the viewport. |
--available-height | The available height between the anchor and the edge of the viewport. |
--anchor-width | The anchor's width. |
--anchor-height | The anchor's height. |
--transform-origin | The coordinates that this element is anchored to. Used for animations and transitions. |
--positioner-width | The width of the menu's positioner.
It is important to set width to this value when using CSS to animate size changes. |
--positioner-height | The height of the menu's positioner.
It is important to set height to this value when using CSS to animate size changes. |
RadioGroupMenu.RadioGroup
Groups related radio items. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
defaultValueThe uncontrolled value of the radio item that should be initially selected. | any | — |
valueThe controlled value of the radio item that should be currently selected. | any | — |
onValueChangeFunction called when the selected value changes. | ((value: any, eventDetails: MenuRootChangeEventDetails) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
childrenThe content of the component. | React.ReactNode | — |
RadioItemMenu.RadioItem
A menu item that works like a radio button in a given group. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
labelOverrides the text used for keyboard text navigation and filtering. | string | — |
valuerequiredValue of the radio item. | any | — |
onClickThe click handler for the menu item. | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
closeOnClickWhether to close the menu when the item is clicked. | boolean | false |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
disabledWhether the component should ignore user interaction. | boolean | false |
| Data attribute | Description |
|---|---|
data-checked | Present when the menu radio item is selected. |
data-unchecked | Present when the menu radio item is not selected. |
data-disabled | Present when the menu radio item is disabled. |
data-highlighted | Present when the menu radio item is highlighted. |
RadioItemIndicatorMenu.RadioItemIndicator
Indicates whether the radio item is selected. Renders a <span>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the HTML element in the DOM when the radio item is inactive. | boolean | false |
| Data attribute | Description |
|---|---|
data-checked | Present when the menu radio item is selected. |
data-unchecked | Present when the menu radio item is not selected. |
data-disabled | Present when the menu radio item is disabled. |
data-starting-style | Present when the radio indicator begins animating in. |
data-ending-style | Present when the radio indicator is animating out. |
RootMenu.Root
Groups all parts of the menu. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
defaultOpenWhether the menu is initially open. | boolean | false |
openWhether the menu is currently open. | boolean | — |
onOpenChangeEvent handler called when the menu is opened or closed. | ((open: boolean, eventDetails: MenuRootChangeEventDetails) => void) | — |
highlightItemOnHoverWhether moving the pointer over items should highlight them. | boolean | true |
actionsRefA ref to imperative actions. | React.RefObject<MenuRootActions | null> | — |
closeParentOnEscWhen in a submenu, determines whether pressing the Escape key | boolean | false |
defaultTriggerIdID of the trigger that the menu is associated with. | string | null | — |
handleA handle to associate the menu with a trigger. | MenuHandle<Payload> | — |
loopFocusWhether to loop keyboard focus back to the first item | boolean | true |
modalDetermines if the menu enters a modal state when open. | boolean | true |
onItemHighlightedCallback fired when an item is highlighted or unhighlighted. | ((highlightedItem: HTMLElement | undefined, eventDetails: MenuRootHighlightEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed. | ((open: boolean) => void) | — |
triggerIdID of the trigger that the menu is associated with. | string | null | — |
disabledWhether the component should ignore user interaction. | boolean | false |
orientationThe visual orientation of the menu. | MenuRootOrientation | 'vertical' |
childrenThe content of the menu. | React.ReactNode | PayloadChildRenderFunction<Payload> | — |
SeparatorMenu.Separator
A separator element accessible to screen readers. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
orientationThe orientation of the separator. | Orientation | 'horizontal' |
| Data attribute | Description |
|---|---|
data-orientation | Indicates the orientation of the separator. |
SubmenuRootMenu.SubmenuRoot
Groups all parts of a submenu. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
defaultOpenWhether the menu is initially open. | boolean | false |
openWhether the menu is currently open. | boolean | — |
onOpenChangeEvent handler called when the menu is opened or closed. | ((open: boolean, eventDetails: MenuRootChangeEventDetails) => void) | — |
highlightItemOnHoverWhether moving the pointer over items should highlight them. | boolean | true |
actionsRefA ref to imperative actions. | React.RefObject<MenuRootActions | null> | — |
closeParentOnEscWhen in a submenu, determines whether pressing the Escape key | boolean | false |
loopFocusWhether to loop keyboard focus back to the first item | boolean | true |
onItemHighlightedCallback fired when an item is highlighted or unhighlighted. | ((highlightedItem: HTMLElement | undefined, eventDetails: MenuRootHighlightEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the menu is opened or closed. | ((open: boolean) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
orientationThe visual orientation of the menu. | MenuRootOrientation | 'vertical' |
childrenThe content of the submenu. | React.ReactNode | — |
SubmenuTriggerMenu.SubmenuTrigger
A menu item that opens a submenu. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
labelOverrides the text used for keyboard text navigation and filtering. | string | — |
onClick | ((event: BaseUIEvent<React.MouseEvent<HTMLDivElement, MouseEvent>>) => void) | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | false |
disabledWhether the component should ignore user interaction. | boolean | false |
openOnHoverWhether the menu should also open when the trigger is hovered. | boolean | true |
delayHow long to wait before the menu may be opened on hover. | number | 100 |
closeDelayHow long to wait before closing the menu that was opened on hover. | number | 0 |
| Data attribute | Description |
|---|---|
data-popup-open | Present when the corresponding submenu is open. |
data-highlighted | Present when the submenu trigger is highlighted. |
data-disabled | Present when the submenu trigger is disabled. |
TriggerMenu.Trigger
A button that opens the menu. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
handleA handle to associate the trigger with a menu. | MenuHandle<Payload> | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
payloadA payload to pass to the menu when it is opened. | Payload | — |
disabledWhether the component should ignore user interaction. | boolean | false |
openOnHoverWhether the menu should also open when the trigger is hovered. | boolean | — |
delayHow long to wait before the menu may be opened on hover. | number | 100 |
closeDelayHow long to wait before closing the menu that was opened on hover. | number | 0 |
children | React.ReactNode | — |
| Data attribute | Description |
|---|---|
data-popup-open | Present when the corresponding menu is open. |
data-pressed | Present when the trigger is pressed. |
data-disabled | Present when the trigger is disabled. |
ViewportMenu.Viewport
A viewport for displaying content transitions. This component is only required if one popup can be opened by multiple triggers, its content changes based on the trigger, and switching between them is animated. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
childrenThe content to render inside the transition container. | React.ReactNode | — |
| Data attribute | Description |
|---|---|
data-current | Applied to the direct child of the viewport when no transitions are present or the new content when it's entering. |
data-previous | Applied to the direct child of the viewport that contains the exiting content when transitions are present. |
data-activation-direction | Indicates the direction from which the popup was activated. This can be used to create directional animations based on how the popup was triggered. Contains space-separated values for both horizontal and vertical axes. |
data-transitioning | Indicates that the viewport is currently transitioning between old and new content. |
data-instant | Present if animations should be instant. |
| CSS variable | Description |
|---|---|
--popup-width | The width of the parent popup. This variable is placed on the 'previous' container and stores the width of the popup when the previous content was rendered. It can be used to freeze the dimensions of the popup when animating between different content. |
--popup-height | The height of the parent popup. This variable is placed on the 'previous' container and stores the height of the popup when the previous content was rendered. It can be used to freeze the dimensions of the popup when animating between different content. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| EnterSpaceArrowDownArrowUp | Opens the menu | Trigger |
| ArrowDown | Opens the menu and focuses the first item | Trigger |
| ArrowUp | Opens the menu and focuses the last item | Trigger |
| ArrowDownArrowUp | Moves to the next or previous item | Popup |
| HomeEnd | Moves to the first or last item | Popup |
Moves to the next item whose text, or label, starts with what was typed | Item | |
| Enter | Activates the highlighted item and closes the menu | Item |
| Space | Checks or unchecks a checkbox item | CheckboxItem |
| Enter | Checks or unchecks a checkbox item | CheckboxItem |
| EnterSpace | Selects a radio item | RadioItem |
| ArrowRight | Opens a submenu and focuses its first item (ArrowLeft in right-to-left) | SubmenuTrigger |
| ArrowLeft | Closes a submenu and returns focus to its trigger | SubmenuTrigger |
| Escape | Closes the menu and keeps focus on the trigger | Popup |
| Escape | In a submenu, closes only the submenu unless closeParentOnEsc is on | SubmenuRoot |
| Tab | Closes the menu and moves focus to the element after the trigger | Popup |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="menu", aria-expanded, aria-controls | a screen reader says the button opens a menu and whether it is open |
| Popup | role="menu" | the popup is announced as a menu |
| Item | role="menuitem" | each action is read as a menu item |
| CheckboxItem | role="menuitemcheckbox", aria-checked | a setting is read with its on or off state |
| RadioItem | role="menuitemradio", aria-checked | the chosen option of a set is read |
| Group | role="group", aria-labelledby to its GroupLabel | related items are read under their label |
| SubmenuTrigger | aria-controls to the submenu | a screen reader knows the item opens another menu |
| Separator | role="separator" | groups are divided for screen readers too |
| CheckboxItemIndicator | aria-hidden="true" | the tick is decoration; the state is on the item |
| Arrow | aria-hidden="true" | the decorative arrow is not read |
Focus order
- Opening with a key moves focus to the first item, or the last with
ArrowUp. - Arrow keys move focus between items; only one item is a tab stop.
Escapecloses the menu and returns focus to Trigger;Tabcloses it and moves on.