A native Android dropdown menu component for React Native (Expo).
It bridges the Android Cascade Popup Menu API (saket/cascade) to React Native, providing smooth submenu transitions and a native look and feel.
Currently supported on Android only.
⚠️ Status: This module is currently in beta.
Contributions and feedback are welcome.
npm install @lhacenmed/react-native-dropdown-menuor
yarn add @lhacenmed/react-native-dropdown-menuHere’s a complete example using expo-router and icons:
import DropdownMenu from "@lhacenmed/react-native-dropdown-menu";
import { Stack } from "expo-router";
import { Text, View } from "react-native";
import { Ionicons } from "@expo/vector-icons";
export default function Index() {
const menuGroups = [
{
items: [
{
id: "export",
title: "Export",
icon: { resource: "ic_menu_save" },
items: [
{
id: "video",
title: "Video",
icon: { resource: "ic_video" },
onSelect: () => console.log("Video"),
},
{
id: "gif",
title: "Gif",
icon: { resource: "ic_gif" },
onSelect: () => console.log("Gif"),
},
],
},
{
id: "share",
title: "Share",
icon: { resource: "ic_menu_share" },
onSelect: () => console.log("Share"),
},
],
},
{
items: [
{
id: "settings",
title: "Settings",
enabled: false,
icon: { resource: "ic_menu_settings" },
},
{
id: "help",
title: "Help",
icon: { resource: "ic_menu_help" },
onSelect: () => console.log("Help"),
},
],
},
];
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Stack.Screen
options={{
title: "Home",
headerRight: () => (
<DropdownMenu
backgroundColor="#161618"
cornerRadius={10}
groups={menuGroups}
>
<View style={{ padding: 8 }}>
<Ionicons name="ellipsis-vertical" size={20} color="#8D8D8D" />
</View>
</DropdownMenu>
),
}}
/>
<Text>Edit app/index.tsx to modify this screen.</Text>
</View>
);
}| Prop | Type | Description |
|---|---|---|
groups |
MenuGroupSpec[] |
The list of menu groups containing menu items. (Required) |
header |
string |
Optional header text shown at the top of the menu. |
backgroundColor |
string |
Background color of the popup menu. |
cornerRadius |
number |
Corner radius for the popup background. |
onSelect |
(info: { id?: string; title: string; path: string[] }) => void |
Called when a menu item is selected. |
trigger |
"press" | "longPress" | "none" |
Defines how the menu opens — by press, long press, or manually. |
anchorTag |
number | null |
(Advanced) Native anchor tag reference for manual positioning. |
wrapperStyle |
ViewStyle |
Custom style for the wrapper around the trigger element. |
children |
React.ReactNode |
The trigger element (button, icon, etc.) that opens the menu. |
disabled |
boolean |
Disables interaction with the trigger. |
type MenuGroupSpec = {
items: MenuItemSpec[];
};type MenuItemSpec = {
id?: string;
title: string;
icon?: {
resource?: string;
base64?: string;
};
enabled?: boolean;
items?: MenuItemSpec[];
onSelect?: (info: { id?: string; title: string; path: string[] }) => void;
};Each item may have:
- A unique
id - A visible
title - An optional
icon(Android resource name or Base64 image) - Nested
itemsfor submenus onSelectcallback for individual actionsenabledflag for disabling items
The native Android module (DropdownMenuModule.kt) uses
CascadePopupMenu to render cascading menus.
It:
- Receives your menu structure from JS via Expo’s module bridge
- Dynamically builds Android
MenuandSubMenuhierarchies - Supports nested items, disabled states, and icons (from resource names or Base64)
- Emits
onItemSelectedevents back to JS when a user selects an item
Internal flow:
DropdownMenu (JS)
↓
Expo Native Module (Kotlin)
↓
CascadePopupMenu → Android View hierarchy
- Platform: Android only (no-op on iOS/web)
- Icon source: Use Android built-in icons (
ic_menu_*) or custom base64 icons - Styling:
backgroundColorandcornerRadiusmap directly to native popup styles
We’re open to contributors! To improve this module or add new features:
- Fork the repository on GitHub
- Clone your fork locally
- Create a new branch for your feature or fix
- Run the example project to test your changes (
cd example && npx expo start) - Submit a pull request with a clear description of your changes
Ensure your code follows existing style and passes lint checks.
MIT © [Lhacen Med]

