apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
Menu
Variant↳ other

Overlays

Menu

Dropdown / context / hover menu primitive — Popover

Overview

<Menu /> is the canonical dropdown / context-menu / hover-menu primitive — Popover's keyboard-driven cousin. Where Popover hosts arbitrary content with focus trapping, Menu is list-shaped: a vertical sequence of focusable actions with full ARIA Menu pattern wiring, arrow-key navigation, type-ahead prefix matching, submenus, checkbox items, radio groups, and destructive item styling — all under a single compound API.

The same component handles four UX patterns: action menus (click an item, do a thing), checkbox menus (multi-select toggles), radio menus (single-select), and submenus (hierarchical menus). The trigger prop unifies dropdown + context + hover into one component instead of shipping three.

Overview — trigger, icons, separator, and destructive action

Loading preview…
Overview.tsx

When to use

  • A list of actions opened from a button: "More options" → Edit / Duplicate / Delete.
  • A right-click context menu on a row, card, file, canvas object.
  • A multi-select control set: "View" → Show sidebar / Show status bar / Fullscreen.
  • A single-select preference: "Theme" → Light / Dark / System.
  • Hierarchical commands: Tools → More → Developer → DevTools.

When NOT to use

  • The content isn't list-shaped → use <Popover /> (arbitrary content) or <Modal /> (blocking).
  • It's a chooser bound to a form field → wait for <Select /> (Phase 23, builds on Menu).
  • It's a hint with no interaction → use <Tooltip />.
  • A "navigation links" component → use a <nav> with <Tabs /> or <Breadcrumbs />.

Anatomy

text
<Menu>
  <Menu.Trigger> button </Menu.Trigger>
  <Menu.Content>
    <Menu.Label> section name </Menu.Label>
    <Menu.Group>
      <Menu.Item>          action               </Menu.Item>
      <Menu.CheckboxItem>  multi-select toggle  </Menu.CheckboxItem>
    </Menu.Group>
    <Menu.Separator />
    <Menu.RadioGroup>
      <Menu.RadioItem value="…"> single-select </Menu.RadioItem>
    </Menu.RadioGroup>
    <Menu.Sub>
      <Menu.SubTrigger> opens submenu </Menu.SubTrigger>
      <Menu.SubContent> nested items </Menu.SubContent>
    </Menu.Sub>
  </Menu.Content>
</Menu>
<Menu>
  <Menu.Trigger> button </Menu.Trigger>
  <Menu.Content>
    <Menu.Label> section name </Menu.Label>
    <Menu.Group>
      <Menu.Item>          action               </Menu.Item>
      <Menu.CheckboxItem>  multi-select toggle  </Menu.CheckboxItem>
    </Menu.Group>
    <Menu.Separator />
    <Menu.RadioGroup>
      <Menu.RadioItem value="…"> single-select </Menu.RadioItem>
    </Menu.RadioGroup>
    <Menu.Sub>
      <Menu.SubTrigger> opens submenu </Menu.SubTrigger>
      <Menu.SubContent> nested items </Menu.SubContent>
    </Menu.Sub>
  </Menu.Content>
</Menu>
  • Menu — context provider + state owner. Owns open, setOpen, the item registry, the keyboard-highlight signal, and the lifecycle hooks (useEscapeStack, useOutsideClick).
  • Menu.Trigger — clones a single child (asChild) or renders an inline <button>. Wires click / contextmenu / pointerenter+leave depending on trigger mode.
  • Menu.Content — portal-rendered, positioned, animated, keyboard-driven floating panel. Carries the visual axes (variant / size / color / placement / offset).
  • Menu.Item — role="menuitem". Fires onSelect for keyboard Enter/Space + mouse click.
  • Menu.CheckboxItem — role="menuitemcheckbox". Toggles via checked / onCheckedChange; forces closeOnSelect=false.
  • Menu.RadioGroup + Menu.RadioItem — exclusive single-pick via value / onValueChange.
  • Menu.Label — visual section heading. role="presentation".
  • Menu.Group — role="group" wrapper for related items.
  • Menu.Separator — role="separator" horizontal rule.
  • Menu.Sub + Menu.SubTrigger + Menu.SubContent — nested submenu.

Trigger modes

ModeOpens onUse case
clickClick (default)Standard dropdown menus
contextRight-click anywhere inside trigger areaContext menus (file, row, canvas)
hoverPointer-enter after openDelayHover-menus (rare; Apple-style)

Switch with <Menu trigger="context">. The whole component works the same way otherwise — same items, same keyboard, same ARIA.

Variants

Three variants of the Content surface (same vocabulary as Popover):

  • solid — paper background + neutral border. Default. color is ignored.
  • outline — paper background + 1px colored border.
  • soft — subtle tinted background + low-opacity colored border.

The 3 variants × 7 colors compound matrix has 14 active cells (solid ignores color; outline and soft use one row each). Adding a new color = palette entry + 2 compound rows.

Item colors

Item color is a constrained axis on purpose:

  • neutral (default) — uses the surface text color.
  • danger — destructive items get red text and a red-tinted highlight.

Most menu items should be neutral; the destructive item is the one canonical exception. For one-off colored items, pass className.

Sizes

SizeItem paddingContent paddingFont
smpx-2 py-1p-1text-xs
mdpx-2 py-1.5p-1text-sm
lgpx-3 py-2p-1.5text-base

Behavior

PropDefaultEffect
closeOnEscapetrueEsc closes topmost menu / submenu (escape-stack ordering).
closeOnOutsideClicktruePointer-down outside trigger + content closes the menu.
closeOnSelecttrue<Menu.Item>'s onSelect closes the menu. CheckboxItem / RadioItem always force false.
looptrueArrow-key wrap at top/bottom.
typeAheadtrueType a prefix to jump to the matching item.
openDelay120 msHover-mode open delay.
closeDelay180 msHover-mode close delay.

Accessibility

ARIA Menu pattern (W3C APG):

  • Trigger — aria-haspopup="menu", aria-expanded, aria-controls, data-state="open|closed".
  • Content — role="menu", aria-labelledby the trigger, aria-orientation="vertical".
  • Item — role="menuitem" (or "menuitemcheckbox" / "menuitemradio"), aria-disabled when disabled, aria-checked for checkbox / radio variants, data-highlighted="true" for the keyboard-highlighted row.
  • Submenu trigger — aria-haspopup="menu", aria-expanded, aria-controls (same pairing pattern as the root trigger).

Keyboard:

KeyAction
ArrowDown / ArrowUpCycle highlight; wrap with loop=true
Home / EndFirst / last enabled item
Enter / SpaceSelect highlighted item
EscClose (innermost only when submenus are open)
Tab / Shift+TabClose (matches platform menu convention)
ArrowRight / ArrowLeftOpen / close submenu
Printable characterType-ahead — prefix match within ~500 ms
Same letter twiceCycle through items starting with that letter (macOS / Windows convention)

Focus management:

  • Focus moves into Content on open (the <menu> element itself receives focus; items are navigated via the keyboard highlight, not by tabbing).
  • Focus returns to the trigger on close.
  • Submenus restore highlight to their parent SubTrigger.

axe-core: zero violations across every variant × color cell, plus disabled / submenu / checkbox / radio combinations.

Examples

Overview

Loading preview…
Overview.tsx

Basic

Loading preview…
Basic.tsx

WithIcons

Loading preview…
WithIcons.tsx

WithShortcuts

Loading preview…
WithShortcuts.tsx

ContextMenu

Loading preview…
ContextMenu.tsx

HoverMenu

Loading preview…
HoverMenu.tsx

DestructiveItem

Loading preview…
DestructiveItem.tsx

CheckboxItems

Loading preview…
CheckboxItems.tsx

RadioGroup

Loading preview…
RadioGroup.tsx

Submenus

Loading preview…
Submenus.tsx

Disabled

Loading preview…
Disabled.tsx

TypeAhead

Loading preview…
TypeAhead.tsx

Variants

Loading preview…
Variants.tsx

Sizes

Loading preview…
Sizes.tsx

Colors

Loading preview…
Colors.tsx

Controlled

Loading preview…
Controlled.tsx

Theming

tsx
defineTheme({
  components: {
    Menu: {
      defaultProps: { /* root behavior props — loop / typeAhead / closeOnSelect */ },
      styleOverrides: {
        content: 'shadow-xl',
        item: 'rounded-md',
        label: '',
        group: '',
        separator: 'bg-border-strong',
        checkboxIndicator: '',
        radioIndicator: '',
        shortcut: 'text-fg-default',
        subTriggerChevron: '',
      },
    },
  },
});
defineTheme({
  components: {
    Menu: {
      defaultProps: { /* root behavior props — loop / typeAhead / closeOnSelect */ },
      styleOverrides: {
        content: 'shadow-xl',
        item: 'rounded-md',
        label: '',
        group: '',
        separator: 'bg-border-strong',
        checkboxIndicator: '',
        radioIndicator: '',
        shortcut: 'text-fg-default',
        subTriggerChevron: '',
      },
    },
  },
});

Per-instance overrides via <Menu.Content className sx style /> merge on top of the theme overrides, which merge on top of the recipe — same precedence as everywhere else in the DS, and re-validated for compound components by Core 18's defaultProps wiring.

Props

PropTypeDefaultDescription
closeDelaynumber—Hover-close delay in ms when `trigger="hover"`. Default: `180`.
closeOnEscapeboolean—Default: `true`. Esc closes the topmost open menu / submenu (escape-stack ordering).
closeOnOutsideClickboolean—Default: `true`. Pointerdown outside trigger + content closes the menu.
closeOnSelectboolean—Default: `true`. Closes after a `<Menu.Item>`'s `onSelect` fires. Set `false` to keep the menu open after a click (useful for "Save and stay" flows). `<Menu.CheckboxItem>` and `<Menu.RadioItem>` override this to `false` internally — toggling a checkbox usually shouldn't close the menu mid-decision.
defaultOpenboolean—Initial `open` for the uncontrolled case. Default: `false`.
onOpenChange(open: boolean) => void—Notified whenever `open` flips (controlled or uncontrolled).
openboolean—Controlled `open`. When omitted, the menu manages its own state via `defaultOpen`.
openDelaynumber—Hover-open delay in ms when `trigger="hover"`. Default: `120`.
triggerenum—How the trigger opens the menu. Default: `'click'`.