Skip to main content
Mantı UI logoMantı UIv0.12.1
Studio

Menu

Menu displays a floating list of actions or choices. Pass the trigger element, describe the rows with items, and handle a selected action with onSelect. Manti manages positioning, focus, keyboard navigation, and dismissal.

Use Popover instead when the floating panel contains form controls or arbitrary interactive content.

Basic usage

Start with an array of commands. Each command needs a unique value and a visible label. onSelect receives the selected value.

import { Button, Menu } from '@manti-ui/react';
import type { MenuItem } from '@manti-ui/react';
import { useState } from 'react';

const items: MenuItem[] = [
  { value: 'new', label: 'New file' },
  { value: 'open', label: 'Open…' },
  { value: 'save', label: 'Save', shortcut: '⌘S' },
];

export default function MenuBasic() {
  const [selected, setSelected] = useState('Nothing selected');

  return (
    <>
      <Menu
        trigger={<Button variant="tertiary">File</Button>}
        items={items}
        onSelect={setSelected}
      />
      <span aria-live="polite">Selected: {selected}</span>
    </>
  );
}

Add shortcut to display a keyboard hint. It only displays the hint; register the actual shortcut separately with useShortcut.

Sizes

size sets the panel's row rhythm: type, padding, and icon size. It matches the scale every other control uses, so a sm trigger can open a sm menu. Submenus inherit the size of the menu that opened them.

Groups and item states

Use a group to label related actions and a separator to divide different sets. An item can also be disabled or use tone: 'danger' for a destructive action.

Useful item properties:

  • icon: leading visual affordance;
  • shortcut: trailing keyboard hint;
  • disabled: visible but unavailable action;
  • tone: 'danger': destructive action styling;
  • onSelect: handler for only that item.

Checkable items

Checkbox items represent independent settings. Radio items represent one choice from a set. Both are controlled: pass checked and update application state in onCheckedChange.

Set closeOnSelect: false when a choice should leave the menu open. Radio exclusivity comes from application state rather than the group itself.

A submenu is another items array nested inside an item. No separate submenu component is required.

Selected: Nothing selected

Submenus may contain more submenus. Keep every value unique across the whole tree. Selecting a leaf closes the complete tree; placement and keyboard routing are handled automatically. If the submenu label is not plain text, provide ariaLabel.

Item reference

MenuItem[] accepts these shapes:

ShapeUse for
{ value, label }Action
{ type: 'checkbox', … }Independent choice
{ type: 'radio', … }One choice from a set
{ type: 'group', label, items }Labelled group
{ type: 'separator' }Divider
{ type: 'submenu', value, label, items }Another level of commands

State and events

The root onSelect observes every selectable item, including leaves inside a submenu. An item's own onSelect runs after the root handler.

The root menu is uncontrolled by default. Use open with onOpenChange only when application state must own whether it is open:

<Menu
  open={open}
  onOpenChange={setOpen}
  trigger={<Button>Account</Button>}
  items={items}
/>

itemProps adds DOM props to one item. getItemProps applies props to every action and submenu trigger. Prefer semantic properties such as disabled and tone before reaching for these lower-level escape hatches.

Use Menu when every row is an action, checkbox, radio, or submenu trigger. Use Popover for ToggleGroup, Input, Select, Slider, forms, explanatory content, or a mixture of controls and commands.

API

PropTypeDefaultDescription
triggerReactElementRequired element that opens the menu. Cloned with the machine trigger props.
itemsMenuItem[]Required recursive contents: commands, checkbox/radio choices, separators, groups, and `{ type: "submenu", value, label, items }` entries.
getItemProps(item: MenuCommand) => MenuItemRootPropsAdd class, style, ARIA, data attributes, or handlers to item roots.
contentPropsHTMLAttributes<HTMLDivElement>Props merged onto the floating menu content.
placement'top' | 'bottom' | 'bottom-center' | 'left' | 'right' | '…-start' | '…-end''bottom-start'Placement relative to the trigger. `bottom-center` is an explicit alias for the centered `bottom` placement.
size'sm' | 'md' | 'lg''md'Row rhythm of the panel: type, padding, and icon size. Submenus inherit it.
onSelect(value: string) => voidCalled with the value of the selected command.
openbooleanControlled open state.
defaultOpenbooleanInitial open state for uncontrolled usage.
onOpenChange(open: boolean) => voidCalled whenever the open state changes.
ariaLabelstringAccessible name for the root menu panel.

Styling

Anatomy

PartSelectorDescription
content[data-scope="menu"][data-part="content"]The translucent dropdown panel holding the items.
item[data-scope="menu"][data-part="item"]A single selectable command.
trigger-item[data-scope="menu"][data-part="trigger-item"]A command that opens a nested menu.
item-icon[data-scope="menu"][data-part="item-icon"]Leading icon or affordance on a command.
item-text[data-scope="menu"][data-part="item-text"]The command label.
item-shortcut[data-scope="menu"][data-part="item-shortcut"]Trailing hint, e.g. a keyboard shortcut.
item-indicator[data-scope="menu"][data-part="item-indicator"]Checked indicator for checkbox and radio items.
submenu-indicator[data-scope="menu"][data-part="submenu-indicator"]Directional affordance shown on a submenu trigger.
item-group-label[data-scope="menu"][data-part="item-group-label"]The heading of a titled group of commands.
separator[data-scope="menu"][data-part="separator"]A divider between groups of items.

Tokens

TokenControls
--manti-menu-min-widthmin width
--manti-menu-max-widthmax width
--manti-menu-paddingpadding
--manti-menu-gapgap
--manti-menu-item-padding-yitem padding y
--manti-menu-item-padding-xitem padding x
--manti-menu-item-gapitem gap
--manti-menu-item-radiusitem radius
--manti-menu-item-font-sizeitem font size
--manti-menu-submenu-min-widthsubmenu min width
--manti-menu-submenu-indicator-sizesubmenu indicator size
--manti-menu-submenu-offsetsubmenu offset
--manti-menu-motion-offsetmotion offset
--manti-menu-z-indexz index

Last updated

Built end-to-end with Manti UI components and design tokens. Manti UI is a framework-agnostic design system on Zag.js.

GitHub