BetaOverlays

Menu

A list of actions, anchored to whatever opened it. The Popover's positioning with rows in it.

Overview

A list of actions, anchored to whatever opened it. The Popover's positioning with rows in it.

First example

<Menu>
<Menu.Trigger asChild>
<Button variant="tertiary">Actions</Button>
</Menu.Trigger>
<Menu.Overlay />
<Menu.Content>
<Menu.Item onPress={rename}>
<Menu.ItemTitle>Renommer</Menu.ItemTitle>
</Menu.Item>
<Menu.Item variant="danger" onPress={remove}>
<Menu.ItemTitle>Supprimer</Menu.ItemTitle>
</Menu.Item>
</Menu.Content>
</Menu>

Anatomy

<Menu>
  <Menu.Trigger>…</Menu.Trigger>
  <Menu.Overlay />
  <Menu.Content>
    <Menu.Label>…</Menu.Label>
    <Menu.Group>
      <Menu.Item>
        <Menu.ItemIndicator />
        <Menu.ItemTitle>…</Menu.ItemTitle>
        <Menu.ItemDescription>…</Menu.ItemDescription>
      </Menu.Item>
    </Menu.Group>
    <Menu.Separator />
  </Menu.Content>
</Menu>
  • Menu — state and resolved style. It renders no node.
  • Menu.Trigger — what opens it, and the rectangle it anchors to.
  • Menu.Overlay — the backdrop, and what closes it on a press outside.
  • Menu.Content — the panel, positioned against the trigger, in a portal.
  • Menu.Label — a heading over a run of rows.
  • Menu.Separator — a rule between two runs of them.
  • Menu.Group — that run, announced as a group.
  • Menu.Item — one action.
  • Menu.ItemTitle / Menu.ItemDescription — its two lines.
  • Menu.ItemIndicator — a fixed box at either end of a row.

Usage

Basic

<Menu>
  <Menu.Trigger asChild>
    <Button variant="tertiary">Actions</Button>
  </Menu.Trigger>
  <Menu.Overlay />
  <Menu.Content>
    <Menu.Item onPress={rename}>
      <Menu.ItemTitle>Renommer</Menu.ItemTitle>
    </Menu.Item>
    <Menu.Item variant="danger" onPress={remove}>
      <Menu.ItemTitle>Supprimer</Menu.ItemTitle>
    </Menu.Item>
  </Menu.Content>
</Menu>

Choosing a row closes the menu, after the caller's onPress has run: a handler that reads the menu's state has to run while there is still a menu.

A row that does not close

<Menu.Item closesOnPress={false} onPress={() => setCount(n => n + 1)}>
  <Menu.ItemTitle>Encore une</Menu.ItemTitle>
</Menu.Item>

For a row that toggles something the reader will want to toggle again.

Headings, groups and rules

<Menu.Label>Ce document</Menu.Label>
<Menu.Group>
  <Menu.Item>…</Menu.Item>
</Menu.Group>

<Menu.Separator />

<Menu.Item variant="danger">
  <Menu.ItemTitle>Supprimer</Menu.ItemTitle>
</Menu.Item>

Menu.Label and Menu.Group draw nothing; Menu.Separator is the rule, and you place it. A menu of four related actions wants none; a menu whose last row is "Supprimer" wants exactly one, above it. Drawing them between every pair and asking for the exceptions is the wrong way round — a menu is short enough that the one place a break belongs is obvious to whoever wrote it, and invisible to the component.

It runs the panel's full inner width rather than lining up with the rows' text: a rule inset to the titles reads as belonging to the row under it, and this one belongs to neither. It is hidden from screen readers, because announcing "separator" between every pair of actions is noise in the one place a menu has to be brisk.

Style as props

<Menu.Content padding={8} />
<Menu.Item paddingVertical={12} />
<Menu.ItemTitle fontSize={18} />

Full RN names, full RN values (R14). Every node takes them.

Root props

Slots

Variants, sizes and colour

This component adds no visual axis of its own. The values it does take are in the generated types above and in the live demo.

Accessibility

The trigger is a button carrying expanded. Rows are menuitem, groups are menu, and Menu.Label is a header so the group it opens is announced with it. The overlay announces nothing — it is the absence of the menu.

Migration from legacy

The legacy component is Menu, with MenuItem.

Legacyv1
<MenuItem title="…" /><Menu.Item><Menu.ItemTitle>…
description="…"<Menu.ItemDescription>
isDestructivevariant="danger"
icon={…}an <Icon> inside <Menu.ItemIndicator>
customAppearance={{ … }}style on the slot that key named

Implementation notes

What it shares

The positioning is the Popover's, hook for hook: utils/placement.ts, hooks/use-anchor-ref.ts, hooks/use-anchored-position.ts, system/anchored/. This is the third component to read them, after the Select and the Popover.

Not here yet

SubMenu. The reference implementation ships it as its own component and it needs a second anchored panel whose trigger is a row of the first — worth its own change rather than a corner of this one.