← All components
overlays / stable

Menu

Actions or options in a popover: icons, shortcuts, destructive items, sections, submenus, check/radio selection.

LIVE PREVIEW
Loading preview…

When to use

  • Row and record actions behind a "more" button.
  • Display options (group by, visible properties) with selection.
  • Right-click menus via ContextMenu — always mirrored in a visible menu.

Consider an alternative

  • Choosing a form value — use Select or ComboBox.
  • Searching many commands — use Command.
  • Navigation between pages — use links.

Import

import { Menu } from '@revido-llc/kit';

Keyboard interaction

  • Enter / Space / ArrowDown on the trigger — open
  • ArrowUp / ArrowDown — move
  • ArrowRight — open submenu
  • Type a letter — jump to item
  • Enter — run
  • Escape — close

Props

Props documented by the Kit. Refer to the TypeScript types for the complete interface, including inherited props.

classNamestring—Classes for the popover surface.
isOpenboolean—Controlled visibility when using a custom trigger instead of MenuTrigger.
offsetnumber—Distance from the trigger in px. Default 6.
onOpenChange((isOpen: boolean) => void)—Visibility changes for a custom trigger (Escape, outside press, dismissal).
placementPlacement—Popover placement relative to the trigger. Default `bottom start`.
triggerRefRefObject<Element>—Anchor for a custom trigger, such as the overflow segment in a TabList.
autoFocusboolean | FocusStrategy—Where the focus should be set.
childrenReactNode | ((item: T) => ReactNode)—The contents of the collection.
defaultSelectedKeys"all" | Iterable<Key>—The initial selected keys in the collection (uncontrolled).
dependenciesreadonly any[]—Values that should invalidate the item cache when using dynamic collections.
disabledKeysIterable<Key>—The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with.
disallowEmptySelectionboolean—Whether the collection allows empty selection.
escapeKeyBehavior"clearSelection" | "none"'clearSelection'Whether pressing the escape key should clear selection in the menu or not. Most experiences should not modify this option as it eliminates a keyboard user's ability to easily clear selection. Only use if the escape key is being handled externally or should not trigger selection clearing contextually.
itemsIterable<T>—Item objects in the collection.
onAction((key: Key, value: T) => void)—Handler that is called when an item is selected.
onClose(() => void)—Handler that is called when the menu should close after selecting an item.
renderDOMRenderFunction<"div", MenuRenderProps>—Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components. Requirements: - You must render the expected element type (e.g. if `<button>` is expected, you cannot render an `<a>`). - Only a single root DOM element can be rendered (no fragments). - You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.
renderEmptyState(() => ReactNode)—Provides content to display when there are no items in the menu.
selectedKeys"all" | Iterable<Key>—The currently selected keys in the collection (controlled).
selectionModeSelectionMode—The type of selection that is allowed in the collection.
shouldCloseOnSelectboolean—Whether the menu should close when the menu item is selected.
shouldFocusWrapboolean—Whether keyboard navigation is circular.

Related exports

MenuTrigger, MenuItem, MenuSection, MenuSeparator, SubmenuTrigger, ContextMenu, useContextMenu

Examples

Row actions

Leading icons, trailing shortcuts, destructive last and separated.

<MenuTrigger>
  <Button variant="ghost" size="icon" aria-label="More actions"><Ellipsis /></Button>
  <Menu onAction={(key) => run(key)}>
    <MenuItem id="edit" leading={{ kind: "icon", icon: <Pencil /> }} shortcut="E">Edit</MenuItem>
    <SubmenuTrigger>
      <MenuItem>Move to</MenuItem>
      <Menu>…</Menu>
    </SubmenuTrigger>
    <MenuSeparator />
    <MenuItem id="delete" leading={{ kind: "icon", icon: <Trash2 /> }} tone="danger">Delete</MenuItem>
  </Menu>
</MenuTrigger>

Icon labels

Use `leading` for shared option icons. Rich children also stay on one line and keep text truncation.

<MenuItem leading={{ kind: "icon", icon: <Pencil /> }}>Edit</MenuItem>
<MenuItem leading={{ kind: "icon", icon: <Copy /> }}>Copy link</MenuItem>

Selection

Single selection uses a trailing check; multiple selection uses a leading checkbox, per section.

<Menu aria-label="Display options">
  <MenuSection title="Group by" selectionMode="single" selectedKeys={groupBy} onSelectionChange={setGroupBy}>
    <MenuItem id="status">Status</MenuItem>
  </MenuSection>
</Menu>

Context menu

Right-click the card. Mirror these actions in a visible menu.

const menu = useContextMenu();
<div {...menu.triggerProps}>…</div>
<ContextMenu {...menu.menuProps} onAction={run}>
  <MenuItem id="open">Open</MenuItem>
</ContextMenu>

Literal anatomy 1

Static component anatomy; bind events in the consuming app.

<MenuTrigger><Button variant="outline">Actions</Button><Menu aria-label="Draft actions"><MenuSection title="Draft"><MenuItem id="open">Open</MenuItem></MenuSection><MenuSeparator /><SubmenuTrigger><MenuItem id="more">More</MenuItem><Menu aria-label="More actions"><MenuItem id="archive">Archive</MenuItem></Menu></SubmenuTrigger></Menu></MenuTrigger>

Literal anatomy 2

Static component anatomy; bind events in the consuming app.

<ContextMenu position={{ x: 0, y: 0 }}><MenuItem id="open">Open</MenuItem></ContextMenu>

Related components