Skip to main content

Dropdown Menu

tsx
import * as DropdownMenu from 'zeego/dropdown-menu'
tsx
import * as DropdownMenu from 'zeego/dropdown-menu'

A menu component anchored to a button pressed by a user.

Platform behavior​

  • Web: Uses Radix UI's unstyled Dropdown Menu component
  • iOS & Android: Uses each platform's built-in native menu component

Features​

Zeego's menu includes Radix UI's features:

  • Can be controlled or uncontrolled.
  • Supports submenus with configurable reading direction.
  • Supports items, labels, & groups of items.
  • Supports checkable items (single or multiple) with an optional indeterminate state.
  • Supports modal and non-modal modes.
  • Customize the side, alignment, offsets, collision handling.
  • Optionally render a pointing arrow.
  • Focus is fully managed.
  • Full keyboard navigation.
  • Typeahead support.
  • Dismissing and layering behavior is highly customizable.

Plus other unique ones:

  • SF Symbols on iOS with color customization
  • Android system icons
  • Images on menu items (remote images coming soon)

Installation​

Make sure you've followed the getting started guide.

Usage​

tsx
import * as DropdownMenu from 'zeego/dropdown-menu'
export function MyMenu() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Label />
<DropdownMenu.Item>
<DropdownMenu.ItemTitle />
</DropdownMenu.Item>
<DropdownMenu.Group>
<DropdownMenu.Item />
</DropdownMenu.Group>
<DropdownMenu.CheckboxItem>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger />
<DropdownMenu.SubContent />
</DropdownMenu.Sub>
<DropdownMenu.Separator />
<DropdownMenu.Arrow />
</DropdownMenu.Content>
</DropdownMenu.Root>
)
}
tsx
import * as DropdownMenu from 'zeego/dropdown-menu'
export function MyMenu() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Label />
<DropdownMenu.Item>
<DropdownMenu.ItemTitle />
</DropdownMenu.Item>
<DropdownMenu.Group>
<DropdownMenu.Item />
</DropdownMenu.Group>
<DropdownMenu.CheckboxItem>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger />
<DropdownMenu.SubContent />
</DropdownMenu.Sub>
<DropdownMenu.Separator />
<DropdownMenu.Arrow />
</DropdownMenu.Content>
</DropdownMenu.Root>
)
}

Web Usage​

As of Zeego v2, any component available on Radix UI's DropdownMenu is also available on Zeego's DropdownMenu, and accepts the same props, when rendered on web.

Since Zeego is directly built on top of Radix UI for Web, you can not use StyleSheet.create objects on Zeego's style props.

Instead, to style items, you can do one of the following:

  1. Use inline style prop
  2. Use className, common for Tailwind users a. You can optionally use CSS file imports or CSS modules
  3. Use the create function to create your own component and style it however you want

Component API​

Root​

Required component at the root of your menu.

PropRequiredDefaultPlatforms
onOpenChangeweb, ios
onOpenWillChangeios

For more web props, see the Radix UI docs for DropdownMenu.Root.

Content​

Holds all of the content for your menu.

See the Radix UI docs for DropdownMenu.Content to see usage for each prop.

PropRequiredDefaultPlatforms
sidebottomweb
sideOffset0web
aligncenterweb
alignOffset0web
collisionPadding0web
avoidCollisionstrueweb

For more web props, see the Radix UI docs for DropdownMenu.Content.

Trigger​

Wraps the trigger for your menu. The content will be anchored to the trigger.

PropRequiredDefaultPlatforms
styleweb, ios, android
actionpressios, android
asChildfalseweb, ios, android

The action can be longPress or press.

For more web props, see the Radix UI docs for DropdownMenu.Trigger.

Item​

Dropdown menu item. Typically a row with text inside of it.

The key prop is required. The same key must not be used more than once in the same menu.

PropRequiredDefaultPlatforms
keyYesweb, ios, android
disabledweb, ios, android
destructiveweb, ios, android
hiddenios, android
styleweb
onSelectweb
textValueweb
onFocusweb
onBlurweb

On web, it will render its children and apply styles. On other platforms, it simply maps to a native menu item.

To render text, use the ItemTitle.

tsx
<DropdownMenu.Item key="item-1" onSelect={() => console.log('item-1 selected')}>
<DropdownMenu.ItemTitle>Item Title</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
tsx
<DropdownMenu.Item key="item-1" onSelect={() => console.log('item-1 selected')}>
<DropdownMenu.ItemTitle>Item Title</DropdownMenu.ItemTitle>
</DropdownMenu.Item>

For more web props, see the Radix UI docs for DropdownMenu.Item.

ItemTitle​

The style prop will optionally style text on web.

PropRequiredDefaultPlatforms
styleweb,
childrenYesweb , ios, android

ItemTitle accepts either a string or React element as the child. A string is the most common usage.

tsx
<DropdownMenu.Item key="cars">
<DropdownMenu.ItemTitle>Cars</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
tsx
<DropdownMenu.Item key="cars">
<DropdownMenu.ItemTitle>Cars</DropdownMenu.ItemTitle>
</DropdownMenu.Item>

React element child​

ItemTitle supports passing a text node as the child. However, you must pass a textValue prop to the parent Item for this to work. It will error otherwise.

tsx
<DropdownMenu.Item
// this is required when ItemTitle has a React element child
textValue="Cars"
key="cars"
>
<DropdownMenu.ItemTitle>
<Text>
Cars
</Text>
<DropdownMenu.ItemTitle>
</DropdownMenu.Item>
tsx
<DropdownMenu.Item
// this is required when ItemTitle has a React element child
textValue="Cars"
key="cars"
>
<DropdownMenu.ItemTitle>
<Text>
Cars
</Text>
<DropdownMenu.ItemTitle>
</DropdownMenu.Item>

This is useful for rendering custom text components on Web. The textValue prop supplied to Item will get used on iOS and Android as the title. On Web, textValue will be used for typeahead purposes, but it will not affect rendering.

ItemIcon​

To render an icon on web, pass the icon component as a child.

For iOS and Android, use the respective platform's name as the prop.

On iOS, it renders an SF Symbol if you provide one.

PropRequiredDefaultPlatforms
iosios
androidIconNameandroid
childrenweb
styleweb
classNameweb
tsx
<DropdownMenu.ItemIcon
ios={{
name: '0.circle.fill', // required
pointSize: 5,
weight: 'semibold',
scale: 'medium',
// can also be a color string. Requires iOS 15+
hierarchicalColor: {
dark: 'blue',
light: 'green',
},
// alternative to hierarchical color. Requires iOS 15+
paletteColors: [
{
dark: 'blue',
light: 'green',
},
],
}}
>
<MySvgIconForWeb />
</DropdownMenu.ItemIcon>
tsx
<DropdownMenu.ItemIcon
ios={{
name: '0.circle.fill', // required
pointSize: 5,
weight: 'semibold',
scale: 'medium',
// can also be a color string. Requires iOS 15+
hierarchicalColor: {
dark: 'blue',
light: 'green',
},
// alternative to hierarchical color. Requires iOS 15+
paletteColors: [
{
dark: 'blue',
light: 'green',
},
],
}}
>
<MySvgIconForWeb />
</DropdownMenu.ItemIcon>

ItemImage​

Renders an image inside of the item. Works on Web and iOS. On Android, it will be ignored.

PropRequiredDefaultPlatforms
sourceweb, ios
styleweb
resizeModeweb
heightweb
widthweb
fadeDuration0web
accessibilityLabelweb

For local images with require or import:

tsx
<DropdownMenu.ItemImage
source={require('./image.png')}
height={300}
width={300}
/>
tsx
<DropdownMenu.ItemImage
source={require('./image.png')}
height={300}
width={300}
/>

For remote images, you can use the source prop with a URL.

tsx
<DropdownMenu.ItemImage
source={{
uri: `https://my-image-url`,
}}
height={300}
width={300}
/>
tsx
<DropdownMenu.ItemImage
source={{
uri: `https://my-image-url`,
}}
height={300}
width={300}
/>

ItemImage with Expo Web / Metro Web​

If you are using Solito, Vite, Next.js, or most web-only frameworks, then this does not apply to you.

However, as of Zeego v2, locally-imported images will not work as-is with Metro Web/Expo Web.

To fix this, you should create a custom ItemImage component which wraps Image from react-native:

tsx
import { Image } from 'react-native'
import * as DropdownMenu from 'zeego/dropdown-menu'
const ItemImage = DropdownMenu.create<
React.ComponentProps<typeof DropdownMenu.ItemImage>
>((props) => {
return <Image {...props} />
}, 'ItemImage')
tsx
import { Image } from 'react-native'
import * as DropdownMenu from 'zeego/dropdown-menu'
const ItemImage = DropdownMenu.create<
React.ComponentProps<typeof DropdownMenu.ItemImage>
>((props) => {
return <Image {...props} />
}, 'ItemImage')

ItemSubtitle​

Receives children as a string. The style prop will optionally style text on web.

PropRequiredDefaultPlatforms
styleweb,
classNameweb
childrenweb , ios

Android menu items do not currently support subtitles.

Group​

Used to group multiple items.

On iOS, items will visually group with a divider like Group Item 1 and Group Item 2 below:

On iOS, you can use the horizontal prop render items like so:

image
PropRequiredDefaultPlatforms
childrenweb , ios, android
horizontalios

To add a title to the group, pass a Label component inside of it:

tsx
<DropdownMenu.Group>
<DropdownMenu.Label>Fernando</DropdownMenu.Label>
</DropdownMenu.Group>
tsx
<DropdownMenu.Group>
<DropdownMenu.Label>Fernando</DropdownMenu.Label>
</DropdownMenu.Group>

For more web props, see the Radix UI docs for DropdownMenu.Group.

CheckboxItem​

Usage is similar to Item with added checkbox features.

PropRequiredDefaultPlatforms
keyYesweb, ios, android
valueYesweb, ios, android
disabledweb , ios, android
destructiveweb , ios, android
onValueChangeweb , ios, android
hiddenweb , ios, android
textValueweb
styleweb
onFocusweb
onBlurweb
tsx
<DropdownMenu.CheckboxItem
value="on" // or "off" or "mixed"
onValueChange={(next, previous) => {
// update state here
}}
key="fernando"
>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>
tsx
<DropdownMenu.CheckboxItem
value="on" // or "off" or "mixed"
onValueChange={(next, previous) => {
// update state here
}}
key="fernando"
>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>

You can also use a boolean for value, as of 1.3.0:

tsx
<DropdownMenu.CheckboxItem
value={true}
onValueChange={(next, previous) => {
// next and previous will still use "on" and "off"
}}
key="fernando"
>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>
tsx
<DropdownMenu.CheckboxItem
value={true}
onValueChange={(next, previous) => {
// next and previous will still use "on" and "off"
}}
key="fernando"
>
<DropdownMenu.ItemIndicator />
</DropdownMenu.CheckboxItem>

For more web props, see the Radix UI docs for DropdownMenu.CheckboxItem.

There are a few subtle differences, such as onValueChange vs onCheckedChange, and the Zeego's result being "on" or "off" instead of true or false in the change callback.

ItemIndicator​

Used inside of CheckboxItem, the ItemIndicator only renders when the item is checked. This lets you conditionally render a checkmark.

You should pass a checkmark icon as a child for web. On iOS and Android, the built-in checkmark will be used instead.

PropRequiredDefaultPlatforms
styleweb
classNameweb
childrenweb
tsx
<DropdownMenu.ItemIndicator>
<CheckmarkIcon /> {/* Renders on Web only */}
</DropdownMenu.ItemIndicator>
tsx
<DropdownMenu.ItemIndicator>
<CheckmarkIcon /> {/* Renders on Web only */}
</DropdownMenu.ItemIndicator>

For more web props, see the Radix UI docs for DropdownMenu.ItemIndicator.

Label​

Renders a label. It won't be focusable using arrow keys.

On iOS & Android, only one label is supported (unless it is inside a submenu). It will be displayed at the top of the menu.

PropRequiredDefaultPlatforms
styleweb
classNameweb
childrenYesweb, ios, android
tsx
<DropdownMenu.Label>My Label</DropdownMenu.Label>
tsx
<DropdownMenu.Label>My Label</DropdownMenu.Label>

Best used within a Group.

For more web props, see the Radix UI docs for DropdownMenu.Label.

Arrow​

Renders an arrow on web only. This helps point the content to the trigger. The Arrow must be rendered inside of Content.

By default, Radix renders the arrow as an <svg> element. You can customize the SVG arrow color by passing a fill prop, className, or style object with a fill property.

caution

Because the arrow is an <svg> element, its style prop is not React Native compatible. Styling it with React Native libraries may not work as expected. If you would like to render a custom styled <View>, use the asChild prop instead of wrapping this component.

PropRequiredDefaultPlatforms
width10web
height5web
fillweb
styleweb
classNameweb
asChildfalseweb

See the Radix UI docs for DropdownMenu.Arrow.

Separator​

Renders a content separator on web only.

PropRequiredDefaultPlatforms
styleweb
classNameweb

See the Radix UI docs for DropdownMenu.Separator.

Sub​

tsx
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger key="sub-menu-trigger">
<DropdownMenu.ItemTitle>Sub Menu</DropdownMenu.ItemTitle>
</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item key="sub-menu-item">
<DropdownMenu.ItemTitle>Sub Menu Item</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>
tsx
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Button />
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger key="sub-menu-trigger">
<DropdownMenu.ItemTitle>Sub Menu</DropdownMenu.ItemTitle>
</DropdownMenu.SubTrigger>
<DropdownMenu.SubContent>
<DropdownMenu.Item key="sub-menu-item">
<DropdownMenu.ItemTitle>Sub Menu Item</DropdownMenu.ItemTitle>
</DropdownMenu.Item>
</DropdownMenu.SubContent>
</DropdownMenu.Sub>
</DropdownMenu.Content>
</DropdownMenu.Root>

Renders the parts of a submenu.

PropRequiredDefaultPlatforms
onOpenChangeweb

SubContent​

See the Radix UI docs for DropdownMenu.SubContent to see usage for each prop.

PropRequiredDefaultPlatforms
sidebottomweb
sideOffset0web
aligncenterweb
alignOffset0web
collisionPadding0web
avoidCollisionstrueweb

SubTrigger​

An item that opens a submenu. Must be rendered inside DropdownMenu.Sub.

PropRequiredDefaultPlatforms
keyYesweb, ios, android
disabledweb , ios, android
destructiveweb , ios, android
hiddenios, android
styleweb
onSelectweb
textValueweb
onFocusweb
onBlurweb

For more web props, see the Radix UI docs for DropdownMenu.SubTrigger.