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
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.
| Legacy | v1 |
|---|---|
<MenuItem title="…" /> | <Menu.Item><Menu.ItemTitle>… |
description="…" | <Menu.ItemDescription> |
isDestructive | variant="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.