Introducing PrimeReact v11-alpha 🎉Discover Now

useMenu

Hook that manages menu state, keyboard navigation, focus tracking, and submenu coordination.

basic-demo

Usage#

import { useMenu } from '@primereact/headless/menu';
import { useMenuSubmenu } from '@primereact/headless/menu/submenu';
const menu = useMenu();
const sub = useMenuSubmenu({ defaultOpen: true });
const subTriggerProps = menu.getSubTriggerProps({ value: 'projects', sub });

useMenu manages focus tracking, keyboard navigation, and submenu orchestration for menu structures. See Primitive for a component-based API.

Features#

  • Inline or popup rendering — spread getListProps() directly for an inline menu, or drive visibility with triggerProps and state.open
  • Composite submenus — composite mode enables hover-to-open nested menus with arrow-key navigation across levels
  • Typed items — getItemProps({ type }) produces menuitem, checkbox, or radio behavior with proper ARIA semantics
  • Dynamic prop getters — getItemProps, getSubTriggerProps, getListProps, and getIndicatorProps emit props and data attributes per element
  • Type-ahead search — typing characters moves focus to the matching item label
  • Popover integration — built on usePopover so positioning, portal, and dismissal are configured through the same options

Working with callbacks#

Controlled popup visibility#

Use open and onOpenChange to drive visibility externally — useful for coordinating with other overlays or toolbar state.

const [open, setOpen] = React.useState(false);
 
const menu = useMenu({
    open,
    onOpenChange: (e) => setOpen(e.value)
});
 
<div {...menu.rootProps}>
    <ul {...menu.getListProps()}>
        <li {...menu.getItemProps({ value: 'item1' })}></li>
        <li {...sub.subProps}>
            <div {...subTriggerProps}></div>
            <ul {...menu.getListProps({ value: 'projects', sub })}></ul>
        </li>
    </ul>
</div>;

Create a separate useMenuSubmenu instance per submenu and wire it through getSubTriggerProps / getListProps.

const menu = useMenu({ composite: true });
const sub = useMenuSubmenu();
const subTriggerProps = menu.getSubTriggerProps({ value: 'files', sub });
const subListProps = menu.getListProps({ value: 'files', sub });

Checkbox and radio items#

Pass type and checked to getItemProps and wrap radio items in an element carrying radioGroupProps.

menu.getItemProps({ value: 'notifications', type: 'checkbox', checked: notificationsOn });
menu.getItemProps({ value: 'light', type: 'radio', checked: theme === 'light' });

Indicator visibility with match#

getIndicatorProps returns a matchVisible flag so you can render submenu arrows, checkmarks, or radio dots conditionally.

const indicatorProps = menu.getIndicatorProps({ type: 'checkbox', checked: true, match: 'checked' });
 
{
    indicatorProps.matchVisible && <CheckIcon {...indicatorProps} />;
}

Styling with data attributes#

All prop getters attach data-scope="menu" and a data-part attribute for CSS targeting. Additional data attributes reflect runtime state:

  • getItemProps — data-part="item", plus data-value, data-focused, data-disabled, data-checked
  • getSubTriggerProps — data-part="subtrigger", plus data-value, data-focused, data-disabled, data-open
  • getIndicatorProps — data-part="indicator", plus data-open/data-closed or data-checked/data-unchecked
  • getListProps — data-part="list"
[data-scope='menu'][data-part='item'][data-focused] {
    background-color: var(--surface-hover);
}
 
[data-scope='menu'][data-part='item'][data-checked] {
    font-weight: 600;
}
 
[data-scope='menu'][data-part='subtrigger'][data-open] {
    background-color: var(--surface-hover);
}
 
[data-scope='menu'][data-part='indicator'][data-checked] {
    color: var(--primary);
}
 
[data-scope='menu'][data-part='item'][data-disabled] {
    opacity: 0.6;
    pointer-events: none;
}

API#

useMenu#

NameTypeDefault
openboolean—
Controlled open state of the menu.
defaultOpenbooleanfalse
Default open state for uncontrolled mode.
compositebooleanfalse
Whether the menu is in composite mode (navigationmenu). In composite mode, submenus open on hover and focusedOptionId is an array tracking the focus path.
appendTo"body" | HTMLElement | "self"'body'
The element to which the overlay is appended.
baseZIndexnumber0
Base zIndex value to use in layering.
autoZIndexbooleantrue
Whether to automatically manage layering.
tabIndexnumber0
Index of the element in tabbing order.
openDelaynumber100
The delay in milliseconds before opening the menu when hovering the trigger. Only applies when `openOnHover` is true.
closeDelaynumber0
The delay in milliseconds before closing the menu when the pointer leaves the trigger. Only applies when `openOnHover` is true.
openOnHoverbooleanfalse
When true, the menu opens on pointer hover over the trigger (in addition to click).
onOpenChange(event: useMenuOpenChangeEvent) => void—
Callback to invoke when the open state changes.
contextuseMenuSubmenuExposes—
Optional parent submenu context whose popover state should drive this menu. When provided, useMenu reuses the parent submenu's popover (anchor, open state, positioner) instead of creating its own. Forwarded by Menu.Root automatically when nested inside a Menu.Submenu.
navigationmenuuseNavigationMenuExposes—
Optional parent NavigationMenu context. When provided, ArrowRight / ArrowLeft pressed on a list item that is not a submenu trigger will move focus to the next / previous sibling navigationmenu trigger (WAI-ARIA NavigationMenu pattern). Forwarded by Menu.Root when nested inside a `NavigationMenu` .
navigationmenuMenuIdstring—
The id this menu is registered under in the parent NavigationMenu's trigger registry. Required for `navigationmenu` sibling navigation to know which trigger is "current".

useMenuSubmenu#

NameTypeDefault
openboolean—
Controlled open state of the submenu.
defaultOpenbooleanfalse
Default open state for uncontrolled mode.
disabledbooleanfalse
Whether the submenu is disabled.
openDelaynumber100
The delay in milliseconds before opening the submenu when hovering the subtrigger. Only applies when `openOnHover` is true.
closeDelaynumber0
The delay in milliseconds before closing the submenu when the pointer leaves the subtrigger. Only applies when `openOnHover` is true.
openOnHoverbooleantrue
When true, the submenu opens on pointer hover over the subtrigger.
onOpenChange(event: useMenuSubmenuOpenChangeEvent) => void—
Callback to invoke when the open state changes.

Accessibility#

Arrow keys navigate items, Enter or Space activates the focused item, Escape closes, and type-ahead matches item labels. See Primitive for full WAI-ARIA compliance details.