Dropdown

Atom
Floating menus triggered by a button: actions, options, navigation.

Playground

Every control sets a real prop of DropdownProps. The JSX below updates as you go.
Props
triggerLabel
string
open
boolean
compact
boolean
alignEnd
boolean
disabled
boolean
JSX · DropdownProps
<Dropdown triggerLabel="Project" open />

Variants

9 examples from src/atoms/dropdown/dropdown.md, each rendered live from its own JSX.
Project menu
<Dropdown compact steadyPress menuWidth={250} trigger="Projects" label="Your projects" note="Only projects you can access appear."
items={[
{ label: "All my projects", value: "all", detail: "11", selected: true },
{ label: "Platform Core", value: "core", detail: "6", selected: false },
{ label: "Add project", value: "add", sectionBefore: "Manage projects", description: "Open project setup" },
]} />
Disabled trigger
<Dropdown
trigger="Actions"
disabled
items={[
{ label: "Edit profile" },
{ label: "Duplicate" },
{ label: "Settings" }
]}
/>
Disabled item
<Dropdown
trigger="Actions"
items={[
{ label: "Edit profile" },
{ label: "Duplicate" },
{ label: "Archive", disabled: true }
]}
/>
Destructive item
<Dropdown
trigger="Actions"
items={[
{ label: "Edit profile" },
{ label: "Duplicate" },
{ label: "Delete…", destructive: true, separatorBefore: true }
]}
/>
Section label
<Dropdown
trigger="Actions"
label="Actions"
items={[
{ label: "Edit profile" },
{ label: "Duplicate" },
{ label: "Settings" }
]}
/>
Identity header
<Dropdown
trigger="Account"
title="Rachel Chen"
description="rachel@nannier.com"
items={[
{ label: "Profile" },
{ label: "Settings" },
{ label: "Log out" }
]}
/>
Keyboard shortcuts
<Dropdown
trigger="Actions"
items={[
{ label: "Edit profile", shortcut: "⌘E" },
{ label: "Duplicate", shortcut: "⌘D" },
{ label: "Settings", shortcut: "⌘," }
]}
/>
Custom trigger
<Dropdown
items={[
{ label: "Profile" },
{ label: "Settings" },
{ label: "Log out" }
]}
>
<Avatar name="Rachel Chen" />
</Dropdown>
End alignment
<Dropdown
trigger="Account"
alignEnd
items={[
{ label: "Profile" },
{ label: "Settings" },
{ label: "Log out" }
]}
/>

Anatomy

1
Trigger
2
Menu
3
Section label
4
Item
5
Shortcut
6
Destructive item
Dimmed parts appear only in some states or variants.

Props

22 props on DropdownProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
22 props
anchorRef
RefObject<View | null>
–
Use an existing trigger without adding a trigger or layout wrapper.
accessibilityLabel
string
–
Explicit accessible name for the menu.
compact
boolean
false
Exact compact web application menu frame and row metrics.
steadyPress
boolean
false
Preserve resting row fill and opacity during a press.
menuWidth
number
–
Width of the floating menu, clamped inside the overlay outlet.
maxHeight
number
–
Height cap for the compact menu's scrolling contents.
note
ReactNode
–
Muted footnote separated from menu commands by a hairline.
trigger
string
–
Label for the default outline trigger button. Omit when supplying a custom trigger via children.
children
ReactNode
–
A custom trigger rendered in place of the default outline button, e.g. an avatar account chip. It is wrapped in a Pressable that toggles the menu; the menu itself still comes from items.
triggerLabel
string
–
The accessible name for a CUSTOM trigger (children). Without it the button is named from its own contents, which runs the trigger's text together into one unpunctuated string ("Rachel Chenrachel@example.com") and repeats any label a nested control already carries. Ignored by the default trigger button, which is named by its own text.
label
string
–
Optional muted section heading rendered above the rows (e.g. "Actions").
title
string
–
Identity header title, rendered in the popover foreground ABOVE everything else in the menu (an account name over its email, say). Pair it with description; passing neither omits the header block entirely.
description
string
–
Identity header second line, muted, under title.
items *
DropdownItem[]
–
The menu rows, top to bottom.
alignEnd
boolean
false
Align the menu's TRAILING edge with the trigger's trailing edge instead of the leading edge (the default). For a trigger parked at the end of a bar, where a leading-aligned menu would run off the surface. Logical, so a right-to-left locale mirrors it.
disabled
boolean
false
Dimmed, inert trigger: the menu never opens and the press is a no-op.
inline
boolean
false
Draw the menu open and in flow under the trigger instead of floating it over the page: a specimen of the menu for a docs page or an empty state. The trigger reads as expanded and the press is inert, a row still fires onSelect, and nothing dismisses the card, so open and onOpenChange have no effect. alignEnd lines the card up with the trigger's trailing edge, as it does for the floating card.
open
boolean
false
Controlled open state. Omit for uncontrolled (the trigger opens/closes it).
onOpenChange
(open: boolean) => void
–
Fired when the open state changes (trigger press, select, etc.).
onSelect
(item: DropdownItem, index: number) => void
–
Fired with the selected item and its index when a row is pressed.
testID
string
–
E2E hook forwarded to the root element.
style
StyleProp<ViewStyle>
–
Outer layout composition only (width/flex within a parent), never a restyle hook.

Accessibility and keyboard

Keyboard
Enter
Space
ArrowDown
Opens the menu
ArrowUp
ArrowDown
Moves between items
Home
End
First · last item
Enter
Chooses the item
Escape
Closes, focus returns to the trigger
Semantics
Roles
button
group
menu
menuitem
radio
States
checked
disabled
expanded
ARIA on web
aria-checked
aria-disabled
aria-expanded
aria-haspopup
aria-label
Below the platform minimum, the touch area grows to 44 pt on iOS and 48 dp on Android without moving the layout.
From the docs
In glass mode the menu is a dense-layer glass card under its trigger (or above it when it fits there); the trigger keeps its place, its press and its accessible name and state, and a selection commits and closes the menu the moment a row is pressed. AvatarMenu and the collapsed Navbar menu are built on Dropdown. Solid mode paints the skin's own menu.

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Web
control 10 · field 10 · card 16 · Instrument Sans
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<Dropdown
trigger="Actions"
items={[
{ label: "Edit profile" },
{ label: "Duplicate" },
{ label: "Settings" }
]}
/>
On the web the menu is the v4 panel: a card with a hairline and a soft shadow, 36-pixel rows with quiet icons that take a raised wash under the pointer at once, a mono uppercase eyebrow over a section, mono shortcuts in their own column, destructive rows in the destructive ink, and a disabled row in the muted ink rather than faded. iOS and Android keep their platform menus (the iOS pull-down menu, the Material 3 menu) in the theme's colours.
In glass mode the menu is a dense-layer glass card under its trigger (or above it when it fits there); the trigger keeps its place, its press and its accessible name and state, and a selection commits and closes the menu the moment a row is pressed. AvatarMenu and the collapsed Navbar menu are built on Dropdown. Solid mode paints the skin's own menu.
compact tightens the web rows to 30 pixels and lets the menu scroll inside maxHeight; with steadyPress a press does not tint the row. Selected rows take the action ink. Items can carry a stored value, detail, description, selected state, and a sectionBefore heading. keepOpen leaves refresh-style commands open. A note adds a muted footer. menuWidth and maxHeight bound the scrolling menu. Pass anchorRef to use an existing trigger without adding another control or layout wrapper; accessibilityLabel names the menu independently of its heading. Checked choices use browser menu-radio semantics and the native radio trait.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--bad
--bls
--bw
--fd
--fm
--ft
--hl
--ln
--ln2
--sf
--sf3
--sf4
--sh-md
--tx
--tx3

Related components