1. Components
  2. Menus and selection
  3. Menu

Menu

A menu displays a list of actions or options that a user can choose.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>Account settings</MenuItem>
    <MenuItem>Create team</MenuItem>
    <MenuItem>Command menu</MenuItem>
    <MenuItem>Log out</MenuItem>
  </Menu>
</MenuRoot>

Installation

npx dotui-cli@latest add menu

Usage

Use Menu to display a list of actions or options that a user can choose.

Overlay type

Use the type prop to set the overlay type. The default is "popover". You can also set the mobile type using the mobileType prop ("drawer" by default).

Type
MobileType
<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu type={type} mobileType={mobileType}>
    <MenuItem>Account settings</MenuItem>
    <MenuItem>Create team</MenuItem>
    <MenuItem>Command menu</MenuItem>
    <MenuItem>Log out</MenuItem>
  </Menu>
</MenuRoot>

Position

Use the placement prop to set the position of the menu relative to the trigger button.

Placement
<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu placement={placement}>
    <MenuItem>Account settings</MenuItem>
    <MenuItem>Create team</MenuItem>
    <MenuItem>Log out</MenuItem>
  </Menu>
</MenuRoot>

Selection mode

Single selection

Set the selectionMode prop to single for single selection.

const [selected, setSelected] = React.useState<Selection>(new Set(["center"]));
return (
  <MenuRoot>
    <Button>Align</Button>
    <Menu selectionMode="single" selectedKeys={selected} onSelectionChange={setSelected}>
      <MenuItem id="start">Start</MenuItem>
      <MenuItem id="center">Center</MenuItem>
      <MenuItem id="end">End</MenuItem>
    </Menu>
  </MenuRoot>
);

Multiple selection

Set the selectionMode prop to multiple for multiple selection.

<MenuRoot>
  <Button>Panels</Button>
  <Menu selectionMode="multiple" defaultSelectedKeys={["sidebar", "searchbar", "console"]}>
    <MenuItem id="sidebar">Sidebar</MenuItem>
    <MenuItem id="searchbar">Searchbar</MenuItem>
    <MenuItem id="tools">Tools</MenuItem>
    <MenuItem id="console">Console</MenuItem>
  </Menu>
</MenuRoot>

Variant

Use the variant prop to set the visual style of the menu item.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>Account settings</MenuItem>
    <MenuItem>Create team</MenuItem>
    <MenuItem>Command menu</MenuItem>
    <MenuItem variant="danger">Delete</MenuItem>
  </Menu>
</MenuRoot>

Label and description

MenuItems also support the "label" and "description" slots to separate primary and secondary content. You can also use the Text component to do the same behaviour.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem label="New file" description="Create a new file" />
    <MenuItem label="Copy link" description="Copy the file link" />
    <MenuItem label="Edit file" description="Allows you to edit the file" />
  </Menu>
</MenuRoot>

Prefix and suffix

To add additional context for the menu item, such as icons, use the prefix and suffix props.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem label="New file" description="Create a new file" prefix={<PlusSquareIcon />} />
    <MenuItem label="Copy link" description="Copy the file link" prefix={<CopyIcon />} />
    <MenuItem label="Edit file" description="Allows you to edit the file" prefix={<SquarePenIcon />} />
  </Menu>
</MenuRoot>

Keyboard shortcut

Use the shortcut prop to add a keyboard shortcut to a menu item, or use the Keyboard component.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem shortcut="⌘N">New file</MenuItem>
    <MenuItem shortcut="⌘C">Copy link</MenuItem>
    <MenuItem shortcut="⌘⇧E">Edit file</MenuItem>
  </Menu>
</MenuRoot>

Alternatively, items may be links to another page or website. This can be achieved by passing the href prop to the <MenuItem> component. Link items in a menu are not selectable.

Disabled

A MenuItem can be disabled with the isDisabled prop.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>Account settings</MenuItem>
    <MenuItem prefix={<PlusSquareIcon />} isDisabled>
      Create team
    </MenuItem>
    <MenuItem>Log out</MenuItem>
  </Menu>
</MenuRoot>

Long press

By default, Menu opens by pressing the trigger element or activating it via the Space or Enter keys. This behavior can be changed by providing "longPress" to the trigger prop.

<MenuRoot trigger="longPress">
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>Account settings</MenuItem>
    <MenuItem>Create team</MenuItem>
    <MenuItem>Log out</MenuItem>
  </Menu>
</MenuRoot>

Sections

Menu supports sections with headings in order to group items. Sections can be used by wrapping groups of MenuItems in a Section component.
A Header element may also be included to label the section or using the title prop on the Section component.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <Section title="Notifications">
      <MenuItem>Push notifications</MenuItem>
      <MenuItem>Badges</MenuItem>
    </Section>
    <Separator />
    <Section title="Panels">
      <MenuItem id="console">Console</MenuItem>
      <MenuItem>Search</MenuItem>
    </Section>
  </Menu>
</MenuRoot>

Separator

Separators may be added between menu items or sections in order to create non-labeled groupings.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>New...</MenuItem>
    <MenuItem>Badges</MenuItem>
    <Separator />
    <MenuItem>Save</MenuItem>
    <MenuItem>Save as...</MenuItem>
    <MenuItem>Rename...</MenuItem>
    <Separator />
    <MenuItem>Page setup…</MenuItem>
    <MenuItem>Print…</MenuItem>
  </Menu>
</MenuRoot>

Submenus can be created by wrapping a MenuItem and a Menu in a MenuSub.

<MenuRoot>
  <Button><MenuIcon /></Button>
  <Menu>
    <MenuItem>Account settings</MenuItem>
    <MenuSub>
      <MenuItem>Invite users</MenuItem>
      <Menu>
        <MenuItem>SMS</MenuItem>
        <MenuItem>Twitter</MenuItem>
        <MenuSub>
          <MenuItem>Email</MenuItem>
          <Menu>
            <MenuItem>Work</MenuItem>
            <MenuItem>Personal</MenuItem>
          </Menu>
        </MenuSub>
      </Menu>
    </MenuSub>
  </Menu>
</MenuRoot>

Controlled

The open state can be controlled via isOpen and onOpenChange props.

state: closed

const [isOpen, setOpen] = React.useState(false);
return (
  <MenuRoot isOpen={isOpen} onOpenChange={setOpen}>
    <Button><MenuIcon /></Button>
    <Menu>
      <MenuItem>Account settings</MenuItem>
      <MenuItem>Create team</MenuItem>
      <MenuItem>Log out</MenuItem>
    </Menu>
  </MenuRoot>
)

Composition

If you need to customize things further, you can drop down to the composition level.

<MenuRoot>
  <Button>
    <MenuIcon />
  </Button>
  <Overlay type="popover" mobileType="drawer">
    <MenuContent>
      <MenuItem>Account settings</MenuItem>
      <MenuItem>Create team</MenuItem>
      <MenuItem>Command menu</MenuItem>
      <MenuItem>Log out</MenuItem>
    </MenuContent>
  </Overlay>
</MenuRoot>

API Reference

PropTypeDefaultDescription
children*React.ReactNode-
trigger'press' | 'longPress''press'How the menu is triggered.
isOpenboolean-Whether the overlay is open by default (controlled).
defaultOpenboolean-Whether the overlay is open by default (uncontrolled).
EventTypeDescription
onOpenChange(isOpen: boolean) => voidHandler that is called when the overlay's open state changes.
PropTypeDefaultDescription
type"popover" | "modal" | "drawer"-The overlay type.
mobileType"popover" | "modal" | "drawer"-The overlay type for mobile screens.
mediaQuerystring"(max-width: 640px)"The media query that determines when the mobile type is used.
placementPlacement-The position of the menu relative to the trigger button.
autoFocusboolean | 'first' | 'last'-Where the focus should be set.
shouldFocusWrapboolean-Whether keyboard navigation is circular.
itemsIterable<T>-Item objects in the collection.
disabledKeysIterable<Key>-The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
selectionMode'none' | 'single' | 'multiple''none'The type of selection that is allowed in the collection.
disallowEmptySelectionboolean-Whether the collection allows empty selection.
selectedKeys'all' | Iterable<Key>-The currently selected keys in the collection (controlled).
defaultSelectedKeys'all' | Iterable<Key>-The initial selected keys in the collection (uncontrolled).
dependenciesany[]-Values that should invalidate the item cache when using dynamic collections.
childrenReactNode | (item: object) => ReactNode-The contents of the collection.
classNamestring-The CSS className for the element.
styleCSSProperties-The inline style for the element.
EventTypeDescription
onAction(key: Key) => voidHandler that is called when an item is selected.
onClose() => voidHandler that is called when the menu should close after selecting an item.
onSelectionChange(keys: Selection) => voidHandler that is called when the selection changes.
onScroll(e: UIEvent<Element>) => voidHandler that is called when a user scrolls.
PropTypeDefaultDescription
idKey-The unique id of the item.
variant'default' | 'success' | 'warning' | 'danger' | 'accent'"default"The visual style of the menu item.
valueobject-The object value that this item represents. When using dynamic collections, this is set automatically.
textValuestring-A string representation of the item's contents, used for features like typeahead.
isDisabledboolean-Whether the item is disabled.
childrenReactNode | (values: MenuItemRenderProps & {defaultChildren: ReactNode | undefined}) => ReactNode-The children of the component. A function may be provided to alter the children based on component state.
classNamestring-The CSS className for the element.
styleCSSProperties | (values: MenuItemRenderProps & {defaultStyle: CSSProperties}) => CSSProperties-The inline style for the element. A function may be provided to compute the style based on component state.
hrefHref-A URL to link to.
hrefLangstring-Hints at the human language of the linked URL.
targetHTMLAttributeAnchorTarget-The target window for the link.
relstring-The relationship between the linked resource and the current page.
downloadboolean | string-Causes the browser to download the linked URL. A string may be provided to suggest a file name.
pingstring-A space-separated list of URLs to ping when the link is followed.
referrerPolicyHTMLAttributeReferrerPolicy-How much of the referrer to send when following the link.
routerOptionsRouterOptions-Options for the configured client side router.
EventTypeDescription
onAction() => voidHandler that is called when the item is selected.
onHoverStart(e: HoverEvent) => voidHandler that is called when a hover interaction starts.
onHoverEnd(e: HoverEvent) => voidHandler that is called when a hover interaction ends.
onHoverChange(isHovering: boolean) => voidHandler that is called when the hover state changes.
PropTypeDefaultDescription
children*React.Element[]-The contents of the SubmenuTrigger. The first child should be an Item (the trigger) and the second child should be the Popover (for the submenu).
delaynumber200The delay time in milliseconds for the submenu to appear after hovering over the trigger.

Accessibility

Keyboard interactions

KeyDescription
Space EnterWhen focus is on the Trigger, opens the menu and focuses the first item. When focus is on an item, activates the focused item.
ArrowDownWhen focus is on the trigger, opens the menu. When focus is on an item, moves focus to the next item.
ArrowUpWhen focus is on the trigger, opens the menu. When focus is on an item, moves focus to the previous item.
ArrowRightWhen focus is on submenu trigger, opens the submenu.
ArrowLeftWhen focus is on submenu trigger, closes the submenu.
EscWhen menu is open, closes the menu and moves focus to the Trigger button.

Last updated on 10/11/2024

dotUI

Bringing singularity to the web.

Built by mehdibha. The source code is available on GitHub.