Kanzo UI
Overlays & feedback

Menu

A dropdown list of actions, with groups, submenus and checkbox or radio selection.

Ark UI

One menu, two triggers. Use the default MenuTrigger when a visible control opens it (a button, an avatar, a "⋯"); use MenuContextTrigger when the thing you open from is a region you right-click. There is no separate context-menu component: swapping the trigger is the whole difference, so everything on this page applies to both.

A menu holds commands, not a value. If closing the surface leaves state behind, you wanted a listbox — Select or Combobox. See the rule.

Usage

import {
  Menu,
  MenuContent,
  MenuItem,
  MenuSeparator,
  MenuShortcut,
  MenuTrigger,
} from "@kanzo-tech/ui";
<Menu>
  <MenuTrigger asChild>
    <Button variant="outline">Actions</Button>
  </MenuTrigger>
  <MenuContent>
    <MenuItem value="claim">Claim</MenuItem>
    <MenuItem value="abandon" variant="destructive">Abandon</MenuItem>
  </MenuContent>
</Menu>

Every item needs a value — it is the id the menu reports through onSelect and the key typeahead navigation matches against.

Anatomy

MenuContent renders the portal and the positioner for you. MenuPositioner is that positioner, exported for a menu whose floating layer you assemble yourself.

Menu
├── MenuTrigger                 (or MenuContextTrigger — one or the other)
└── MenuContent                 (portal + positioner)
    ├── MenuGroup
    │   ├── MenuGroupLabel
    │   └── MenuItem
    │       └── MenuShortcut
    ├── MenuCheckboxItem
    ├── MenuRadioGroup
    │   └── MenuRadioItem
    ├── MenuSeparator
    ├── MenuArrow                (optional — the menu draws none by default)
    └── MenuSub
        ├── MenuSubTrigger
        └── MenuSubContent

MenuArrow is the same part Popover, Tooltip and HoverCard ship, under the menu machine: place it inside MenuContent when the menu should point at what opened it.

Groups

MenuGroup takes a heading string and renders the MenuGroupLabel for you.

Checkbox items

Set closeOnSelect={false} on the root when the menu is a set of toggles — otherwise it shuts after the first one and the user has to reopen it for each column.

Radio group

Context trigger

A context menu is this same Menu with one part swapped: MenuContextTrigger opens on right-click at the pointer, where MenuTrigger anchors to a control. Nothing else changes — the content, the items, the groups, the submenus and the shortcuts are the parts above, so anything true of MenuItem is true of an item in a context menu.

import { Menu, MenuContent, MenuContextTrigger, MenuItem } from "@kanzo-tech/ui";
<Menu>
  <MenuContextTrigger asChild>
    <div>Q-1041 · Something is eating the bell-ropes</div>
  </MenuContextTrigger>
  <MenuContent>
    <MenuItem value="claim">Claim</MenuItem>
  </MenuContent>
</Menu>

Reach for it when the target is an area rather than a control — a row, a canvas, a file in a tree. But right-click is not reachable by keyboard on its own, so a context menu should never be the only route to an action: mirror the items in a toolbar Menu.

Keyboard

KeyDoes
/ On the trigger, opens the menu with the first / last item highlighted; inside, moves the highlight.
Home / EndHighlights the first / last item.
Enter / SpaceRuns the highlighted item and closes the menu, or opens its submenu when the item is a MenuSubTrigger.
AZTypeahead: highlights the next item whose text starts with what you have typed. Space joins the query once one is in progress.
Opens the highlighted item's submenu and moves into it.
Closes a submenu and returns the highlight to the parent menu.
EscCloses the menu and returns focus to the trigger.

A checkbox or radio item toggles under Enter like any other item, and closes the menu with it unless you pass closeOnSelect={false}.

API Reference

menuContentVariants is exported for a surface that should read as a menu without being one.

PropTypeDefault
positioningPositioningOptions{ placement: "bottom-end" }
lazyMountbooleantrue
unmountOnExitbooleantrue

Extends React.ComponentProps<typeof ArkMenu.Root>.

Neither takes props of its own; both extend their Ark part (ArkMenu.Trigger, ArkMenu.ContextTrigger) and both take asChild. MenuContextTrigger adds cursor-default, so the region it wraps does not read as a link or a button — the affordance is the right-click. Use one or the other, not both.

PropTypeDefault
variant"default" | "destructive""default"

MenuQuickItem is the same item stacked vertically with a larger icon, for the grid-style "quick action" rows.

PropTypeDefault
headingstring

Notes

MenuContent caps itself at a fixed max-h-96 rather than the floating-ui available-height variable. Clamping to the measured space feeds the element's own resize back into the positioner — content shrinks, the scrollbar toggles, the available space changes, recompute — and that loop hard-freezes the tab. A fixed cap is decoupled from the measurement.

There is no ContextMenu family. A context menu is a Menu whose trigger is a MenuContextTrigger — the only part a right-click surface actually needs, since every item, separator and group below it is the same part either way. A parallel family would have been nine data-slot renames and one real trigger.

On this page