Menu

A menu of actions opened from a button: roving focus, typeahead, checkbox and radio items, submenus.

WAI-ARIA pattern

Installation

npx nuecrea-ui add menu

Usage

import { Button } from '@/components/ui/button';
import * as Menu from '@/components/ui/menu';

export default function MenuStory() {
  return (
    <main className="flex gap-3">
      <Menu.Root>
        <Menu.Trigger render={<Button variant="outline" />}>
          Edit
        </Menu.Trigger>
        <Menu.Portal>
          <Menu.Positioner align="start">
            <Menu.Popup>
              <Menu.Item>Undo</Menu.Item>
              <Menu.Item disabled>
                Redo
              </Menu.Item>
              <Menu.Item>Duplicate</Menu.Item>
              <Menu.Separator />
              <Menu.Group>
                <Menu.GroupLabel>View</Menu.GroupLabel>
                <Menu.CheckboxItem defaultChecked>
                  <Menu.CheckboxItemIndicator />
                  Show grid
                </Menu.CheckboxItem>
                <Menu.CheckboxItem>
                  <Menu.CheckboxItemIndicator />
                  Show rulers
                </Menu.CheckboxItem>
                <Menu.CheckboxItem defaultChecked disabled>
                  <Menu.CheckboxItemIndicator />
                  Show guides
                </Menu.CheckboxItem>
              </Menu.Group>
              <Menu.Separator />
              <Menu.RadioGroup defaultValue="100">
                <Menu.GroupLabel>Zoom</Menu.GroupLabel>
                {['50', '100', '200'].map((v) => (
                  <Menu.RadioItem key={v} value={v}>
                    <Menu.RadioItemIndicator />
                    {v}%
                  </Menu.RadioItem>
                ))}
              </Menu.RadioGroup>
              <Menu.Separator />
              <Menu.SubmenuRoot>
                <Menu.SubmenuTrigger>Share</Menu.SubmenuTrigger>
                <Menu.Portal>
                  <Menu.Positioner>
                    <Menu.Popup>
                      <Menu.Item>Copy link</Menu.Item>
                      <Menu.Item>Email</Menu.Item>
                      <Menu.Item>Slack</Menu.Item>
                    </Menu.Popup>
                  </Menu.Positioner>
                </Menu.Portal>
              </Menu.SubmenuRoot>
              <Menu.Item>Delete</Menu.Item>
            </Menu.Popup>
          </Menu.Positioner>
        </Menu.Portal>
      </Menu.Root>
      <Menu.Root>
        <Menu.Trigger disabled render={<Button variant="outline" />}>
          Locked
        </Menu.Trigger>
      </Menu.Root>
      {/* Room to scroll the trigger out of view (data-anchor-hidden). */}
      <div aria-hidden="true" className="h-[1200px] w-px" />
    </main>
  );
}

API reference

Menu.Root

Owns open state and highlighted item. Renders nothing.

PropTypeDefaultDescription
openboolean—Controlled open state.
defaultOpenbooleanfalseInitial open state.
loopFocusbooleantrueArrow keys wrap at the ends.
highlightItemOnHoverbooleantrueHover highlights (and focuses) items.
disabledbooleanfalseThe menu can't open.
EventPayloadDescription
onOpenChangebooleanAngular: the `open` model's change event.
onOpenChangeCompletebooleanAfter the animation finishes.

Menu.Trigger

Opens the menu: click (focus the menu), Enter/Space/ArrowDown (first item), ArrowUp (last item). Renders <button>.

Data attributePresent when
data-popup-openopen
data-pressedopen after a pointer press
data-disableddisabled

Menu.Portal

Moves the menu to the end of <body>. Renders <div>.

Menu.Positioner

Positions the menu; same options as Popover. Submenus default to the right, aligned to start. Renders <div> with role presentation.

Data attributePresent when
data-openopen
data-closedclosed / animating out
data-anchor-hiddentrigger scrolled out of view
data-alignalways: resolved alignment
data-sidealways: resolved side
data-instant'click' when opened from the keyboard
data-nesteda submenu's positioner

Menu.Item

An action. Closes the menu when activated (closeOnClick). Renders <div> with role menuitem.

PropTypeDefaultDescription
disabledbooleanfalseFocusable with arrows but can't be activated.
closeOnClickbooleantrueClose the menu on activation.
Data attributePresent when
data-highlightedhighlighted (keyboard or hover)
data-disableddisabled

Menu.Separator

A divider. Renders <div> with role separator.

Data attributePresent when
data-orientationalways: horizontal

Menu.Group

Groups items; labelled by its GroupLabel. Renders <div> with role group.

Menu.GroupLabel

Labels a Group or RadioGroup. Renders <div>.

Menu.CheckboxItem

Toggles a setting; stays open (closeOnClick false). Renders <div> with role menuitemcheckbox.

PropTypeDefaultDescription
checkedboolean—Controlled.
defaultCheckedbooleanfalseInitial.
disabledbooleanfalseCan't be toggled.
closeOnClickbooleanfalseClose on toggle.
EventPayloadDescription
onCheckedChangebooleanAngular: the `checked` model's change event.
Data attributePresent when
data-checkedchecked
data-uncheckednot checked
data-highlightedhighlighted (keyboard or hover)
data-disableddisabled

Menu.CheckboxItemIndicator

Shown while checked (styled: a check icon). Renders <span>.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep while unchecked.
Data attributePresent when
data-checkedchecked
data-uncheckednot checked
data-disableddisabled
data-highlightedhighlighted (keyboard or hover)
data-starting-styleanimating in
data-ending-styleanimating out

Menu.RadioGroup

Holds the selected value of its radio items. Renders <div> with role group.

PropTypeDefaultDescription
valueunknown—Controlled.
defaultValueunknown—Initial.
disabledbooleanfalseDisable all items.
EventPayloadDescription
onValueChangeunknownAngular: the `value` model's change event.

Menu.RadioItem

Selects its value in the group; stays open. Renders <div> with role menuitemradio.

PropTypeDefaultDescription
valueunknown—Required.
disabledbooleanfalseCan't be selected.
closeOnClickbooleanfalseClose on select.
Data attributePresent when
data-checkedchecked
data-uncheckednot checked
data-highlightedhighlighted (keyboard or hover)

Menu.RadioItemIndicator

Shown while selected (styled: a dot). Renders <span>.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep while unselected.
Data attributePresent when
data-checkedchecked
data-uncheckednot checked
data-highlightedhighlighted (keyboard or hover)
data-starting-styleanimating in
data-ending-styleanimating out

Keyboard

KeyAction
Enter / Space / ArrowDownOn the trigger: open with the first item focused.
ArrowUpOn the trigger: open with the last item focused.
ArrowDown / ArrowUpMove between items (disabled included), wrapping.
Home / EndFirst / last item.
LettersTypeahead: jump to the next item starting with the typed text.
Enter / SpaceActivate the item.
ArrowRight / ArrowLeftOpen / close a submenu.
EscapeClose (a submenu closes only itself).
TabClose the menu; focus returns to the trigger.