Dropdown Menu
A versatile dropdown menu component with nested sub-menus, positioning, and animations.
Installation
Install the component:
npx natively-ui add dropdown-menu
Usage
DropdownMenuDemo.jsx
1import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
2
3export default function MyComponent() {
4 return (
5 <DropdownMenu>
6 <DropdownMenuTrigger>
7 <Button>Open Menu</Button>
8 </DropdownMenuTrigger>
9 <DropdownMenuContent>
10 <DropdownMenuItem onPress={() => console.log('Item pressed')}>
11 Menu Item
12 </DropdownMenuItem>
13 </DropdownMenuContent>
14 </DropdownMenu>
15 );
16}
Components
DropdownMenu
The root container for the dropdown menu.
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The content of the dropdown menu. |
DropdownMenuTrigger
The element that triggers the dropdown menu.
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The trigger element content. |
asChild | boolean | false | When true, merges props with the first child element instead of wrapping it. |
DropdownMenuContent
The dropdown menu content container.
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The menu items and content. |
className | string | "" | Additional CSS classes for styling. |
sideOffset | number | 4 | Distance between trigger and content. |
align | 'start' | 'center' | 'end' | 'start' | Alignment of the dropdown relative to trigger. |
DropdownMenuItem
An individual menu item.
Prop | Type | Default | Description |
---|---|---|---|
children | ReactNode | - | The menu item content. |
className | string | "" | Additional CSS classes for styling. |
disabled | boolean | false | Whether the menu item is disabled. |
onPress | () => void | - | Function called when the item is pressed. |
Features
- Smooth animations with React Native Reanimated
- Smart positioning that adjusts to screen boundaries
- Nested sub-menus with portal rendering
- Keyboard shortcuts display support
- Menu items with disabled states
- Customizable alignment and spacing
- Dark mode support
- Scrollable content for long menus
- Auto-close on item selection or outside press
Examples
Basic Menu
BasicMenuDemo.jsx
1<DropdownMenu>
2 <DropdownMenuTrigger>
3 <Button>Options</Button>
4 </DropdownMenuTrigger>
5 <DropdownMenuContent>
6 <DropdownMenuItem onPress={() => console.log('Profile')}>
7 <Text>Profile</Text>
8 </DropdownMenuItem>
9 <DropdownMenuSeparator />
10 <DropdownMenuItem onPress={() => console.log('Settings')}>
11 <Text>Settings</Text>
12 </DropdownMenuItem>
13 <DropdownMenuItem disabled>
14 <Text>Disabled Item</Text>
15 </DropdownMenuItem>
16 </DropdownMenuContent>
17</DropdownMenu>
With Icons and Shortcuts
IconMenuDemo.jsx
1<DropdownMenu>
2 <DropdownMenuTrigger asChild>
3 <Button variant="outline">
4 <Icon name="more-horizontal" size={16} />
5 </Button>
6 </DropdownMenuTrigger>
7 <DropdownMenuContent>
8 <DropdownMenuLabel>Actions</DropdownMenuLabel>
9 <DropdownMenuSeparator />
10
11 <DropdownMenuItem onPress={() => console.log('Copy')}>
12 <Icon name="copy" size={16} style={{ marginRight: 8 }} />
13 <Text>Copy</Text>
14 <DropdownMenuShortcut>⌘+C</DropdownMenuShortcut>
15 </DropdownMenuItem>
16
17 <DropdownMenuItem onPress={() => console.log('Cut')}>
18 <Icon name="scissors" size={16} style={{ marginRight: 8 }} />
19 <Text>Cut</Text>
20 <DropdownMenuShortcut>⌘+X</DropdownMenuShortcut>
21 </DropdownMenuItem>
22
23 <DropdownMenuItem onPress={() => console.log('Paste')}>
24 <Icon name="clipboard" size={16} style={{ marginRight: 8 }} />
25 <Text>Paste</Text>
26 <DropdownMenuShortcut>⌘+V</DropdownMenuShortcut>
27 </DropdownMenuItem>
28 </DropdownMenuContent>
29</DropdownMenu>
Sub Menu
SubMenuDemo.jsx
1<DropdownMenu>
2 <DropdownMenuTrigger>
3 <Button>Menu with Submenu</Button>
4 </DropdownMenuTrigger>
5 <DropdownMenuContent>
6 <DropdownMenuItem>
7 <Text>New File</Text>
8 </DropdownMenuItem>
9
10 <DropdownMenuSub>
11 <DropdownMenuSubTrigger>
12 <Icon name="share" size={16} style={{ marginRight: 8 }} />
13 <Text>Share</Text>
14 </DropdownMenuSubTrigger>
15 <DropdownMenuPortal>
16 <DropdownMenuSubContent>
17 <DropdownMenuItem onPress={() => console.log('Email')}>
18 <Icon name="mail" size={16} style={{ marginRight: 8 }} />
19 <Text>Email</Text>
20 </DropdownMenuItem>
21 <DropdownMenuItem onPress={() => console.log('Message')}>
22 <Icon name="message-square" size={16} style={{ marginRight: 8 }} />
23 <Text>Message</Text>
24 </DropdownMenuItem>
25 <DropdownMenuItem onPress={() => console.log('Copy Link')}>
26 <Icon name="link" size={16} style={{ marginRight: 8 }} />
27 <Text>Copy Link</Text>
28 </DropdownMenuItem>
29 </DropdownMenuSubContent>
30 </DropdownMenuPortal>
31 </DropdownMenuSub>
32
33 <DropdownMenuSeparator />
34 <DropdownMenuItem>
35 <Text>Delete</Text>
36 </DropdownMenuItem>
37 </DropdownMenuContent>
38</DropdownMenu>
Alignment Options
AlignmentDemo.jsx
1<View style={{ gap: 16, flexDirection: 'row' }}>
2 {/* Start aligned */}
3 <DropdownMenu>
4 <DropdownMenuTrigger>
5 <Button>Start</Button>
6 </DropdownMenuTrigger>
7 <DropdownMenuContent align="start">
8 <DropdownMenuItem><Text>Left aligned</Text></DropdownMenuItem>
9 <DropdownMenuItem><Text>Menu item</Text></DropdownMenuItem>
10 </DropdownMenuContent>
11 </DropdownMenu>
12
13 {/* Center aligned */}
14 <DropdownMenu>
15 <DropdownMenuTrigger>
16 <Button>Center</Button>
17 </DropdownMenuTrigger>
18 <DropdownMenuContent align="center">
19 <DropdownMenuItem><Text>Center aligned</Text></DropdownMenuItem>
20 <DropdownMenuItem><Text>Menu item</Text></DropdownMenuItem>
21 </DropdownMenuContent>
22 </DropdownMenu>
23
24 {/* End aligned */}
25 <DropdownMenu>
26 <DropdownMenuTrigger>
27 <Button>End</Button>
28 </DropdownMenuTrigger>
29 <DropdownMenuContent align="end">
30 <DropdownMenuItem><Text>Right aligned</Text></DropdownMenuItem>
31 <DropdownMenuItem><Text>Menu item</Text></DropdownMenuItem>
32 </DropdownMenuContent>
33 </DropdownMenu>
34</View>
As Context Menu
ContextMenuDemo.jsx
1function ContextMenuExample() {
2 const [selectedItem, setSelectedItem] = useState(null);
3
4 return (
5 <View style={{ padding: 20 }}>
6 <DropdownMenu>
7 <DropdownMenuTrigger asChild>
8 <Pressable
9 style={{
10 padding: 20,
11 backgroundColor: '#f3f4f6',
12 borderRadius: 8,
13 borderWidth: 2,
14 borderStyle: 'dashed',
15 borderColor: '#d1d5db'
16 }}
17 >
18 <Text style={{ textAlign: 'center' }}>
19 Right click me (or tap on mobile)
20 </Text>
21 </Pressable>
22 </DropdownMenuTrigger>
23
24 <DropdownMenuContent>
25 <DropdownMenuGroup>
26 <DropdownMenuLabel>Actions</DropdownMenuLabel>
27 <DropdownMenuItem onPress={() => setSelectedItem('edit')}>
28 <Text>Edit</Text>
29 </DropdownMenuItem>
30 <DropdownMenuItem onPress={() => setSelectedItem('duplicate')}>
31 <Text>Duplicate</Text>
32 </DropdownMenuItem>
33 </DropdownMenuGroup>
34
35 <DropdownMenuSeparator />
36
37 <DropdownMenuItem
38 onPress={() => setSelectedItem('delete')}
39 className="text-red-600"
40 >
41 <Text style={{ color: '#dc2626' }}>Delete</Text>
42 </DropdownMenuItem>
43 </DropdownMenuContent>
44 </DropdownMenu>
45
46 {selectedItem && (
47 <Text style={{ marginTop: 16 }}>
48 Selected: {selectedItem}
49 </Text>
50 )}
51 </View>
52 );
53}