A native menu component for React Native that provides platform-specific context menus for both Android and iOS. Pass any custom component as a child to trigger native menus.
iOS (Native UIMenu) |
Android (Modal Dialog) |
- âś… Native context menu implementation (UIMenu on iOS, Modal on Android)
- âś… Custom trigger components - pass any React Native component as a child
- âś… Customizable colors for menu items
- âś… Checkmark support with custom colors
- âś… Scrollable menus for long lists
- âś… Event handling for menu item selection
- âś… TypeScript support
- âś… Fabric (New Architecture) compatible
npm install react-native-menus
# or
yarn add react-native-menusFor iOS, run:
cd ios && pod installNo additional setup required for Android.
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { MenuView } from 'react-native-menus';
const App = () => {
const [selectedTheme, setSelectedTheme] = useState('system');
const handleMenuSelect = (event: {
nativeEvent: { identifier: string; title: string };
}) => {
setSelectedTheme(event.nativeEvent.identifier);
console.log('Selected:', event.nativeEvent.title);
};
return (
<View style={styles.container}>
<MenuView
checkedColor="#007AFF"
uncheckedColor="#8E8E93"
menuItems={[
{ identifier: 'light', title: 'Light Mode' },
{ identifier: 'dark', title: 'Dark Mode' },
{ identifier: 'system', title: 'System Default' },
]}
onMenuSelect={handleMenuSelect}
>
<View style={styles.menuButton}>
<Text style={styles.menuButtonText}>
🌓 Theme: {selectedTheme}
</Text>
</View>
</MenuView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
menuButton: {
backgroundColor: '#fff',
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
borderWidth: 1,
borderColor: '#ddd',
},
menuButtonText: {
fontSize: 16,
color: '#333',
},
});
export default App;Use the selectedIdentifier prop to fully control which item is marked as selected. Update it in your onMenuSelect handler to keep iOS and Android behavior consistent.
const [selectedSort, setSelectedSort] = useState('date');
<MenuView
selectedIdentifier={selectedSort}
menuItems={[
{ identifier: 'date', title: 'Date' },
{ identifier: 'name', title: 'Name' },
{ identifier: 'size', title: 'Size' },
]}
onMenuSelect={({ nativeEvent }) => setSelectedSort(nativeEvent.identifier)}
>
<View style={styles.menuButton}>
<Text>📊 Sort by: {selectedSort}</Text>
</View>
</MenuView><MenuView
menuItems={[
{ identifier: 'profile', title: 'View Profile' },
{ identifier: 'settings', title: 'Settings' },
{ identifier: 'logout', title: 'Logout' },
]}
onMenuSelect={handleMenuSelect}
>
<View style={styles.customButton}>
<Text style={styles.customButtonText}>👤 Account Menu</Text>
</View>
</MenuView><MenuView
checkedColor="#5856D6"
menuItems={[
{ identifier: 'opt1', title: 'Option 1' },
{ identifier: 'opt2', title: 'Option 2' },
// ... many more items
{ identifier: 'opt20', title: 'Option 20' },
]}
onMenuSelect={handleMenuSelect}
>
<View style={styles.menuButton}>
<Text>đź“‹ Select Option</Text>
</View>
</MenuView>| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children |
ReactNode |
Yes | - | The trigger component that opens the menu when tapped |
menuItems |
MenuItem[] |
Yes | [] |
Array of menu items to display |
onMenuSelect |
(event: MenuSelectEvent) => void |
No | - | Callback fired when a menu item is selected |
selectedIdentifier |
string |
No | - | Controlled selected item identifier; shows a native checkmark for the matching item |
checkedColor |
string |
No | #007AFF |
Color for checked/selected menu items (Android only) |
uncheckedColor |
string |
No | #8E8E93 |
Color for unchecked/unselected menu items (Android only) |
color |
string |
No | - | Reserved for future use |
style |
ViewStyle |
No | - | Style applied to the container view |
interface MenuItem {
identifier: string; // Unique identifier for the menu item
title: string; // Display text for the menu item
}interface MenuSelectEvent {
identifier: string; // The identifier of the selected menu item
title: string; // The title of the selected menu item
}The MenuView component accepts any React Native component as a child, which becomes the trigger for opening the menu. When tapped, a native context menu appears with the specified menu items.
File: android/src/main/java/com/menu/MenuView.kt
- Container:
FrameLayoutthat accepts child views from React Native - Touch Handling: Intercepts all touch events at the parent level using
onInterceptTouchEvent() - UI: Modal dialog with white background, rounded corners, and bottom positioning
- Menu Items: Implemented as
RadioButtonelements with custom styling - Colors: Full support for
checkedColoranduncheckedColorcustomization - Dividers: 0.5px light gray dividers between menu items
- Scrolling: Automatic scrolling for long lists (max 90% of screen height)
- Selection: Visual feedback with radio button selection states
Key Implementation Details:
- Uses
ViewGroupManagerto support child views - Touch events are intercepted to ensure child views don't block menu opening
- Modal appears at bottom with horizontal margins for mobile-friendly UX
File: ios/MenuView.mm
- Container:
RCTViewComponentView(Fabric architecture) - Child Mounting: Uses
mountChildComponentViewandunmountChildComponentViewfor Fabric compatibility - UI: Native
UIMenuattached to an invisibleUIButtonoverlay - Menu Items: Native
UIActionelements with system styling - Trigger: Creates transparent button overlay on top of child view to show native menu
- Checkmarks: Controlled via
selectedIdentifierand rendered usingUIMenuElementStateOn - Selection: Emits
onMenuSelectwithout mutating native state; updateselectedIdentifierin React to reflect changes
Key Implementation Details:
- Disables user interaction on child views recursively
- Creates invisible button overlay that shows
UIMenuon tap - Fully native iOS 14+ context menu appearance
- Supports both button and non-button child components
| Feature | iOS | Android |
|---|---|---|
| Menu Style | Native UIMenu popover | Modal dialog at bottom |
| Checkmark Color | System default (not customizable) | Fully customizable |
| Unchecked Color | System default | Fully customizable |
| Animation | Native iOS animation | Slide up animation |
| Scrolling | Native UIMenu scrolling | Custom ScrollView (max 40% screen) |
| Selection State | Controlled via selectedIdentifier |
use selectedIdentifier for cross-platform parity |
| Appearance | iOS system theme | White background with rounded corners |
The repository includes a complete example project with 6 different use cases:
- Theme Selector - Shows state management with current selection
- Sort Options - Demonstrates sorting menu with dynamic labels
- File Actions - Common file operations menu
- Priority Selector - Priority level selection
- Custom Trigger - Fully custom styled button
- Long List - Scrollable menu with many items
cd example
yarn install
# For iOS
cd ios && pod install && cd ..
yarn ios
# For Android
yarn android- React Native >= 0.68.0
- iOS >= 14.0 (for UIMenu support)
- Android API >= 21
iOS: Make sure you're running iOS 14 or later, as UIMenu is only available from iOS 14+.
Android: Ensure your child component doesn't have onPress or other touch handlers that might interfere. The MenuView intercepts all touch events at the parent level.
Pass and update selectedIdentifier. iOS does not shift the checkmark automatically—reflect selection in props via your onMenuSelect handler.
The MenuView component requires a child component to act as the trigger. Always wrap your trigger in the MenuView:
// âś… Correct
<MenuView menuItems={items}>
<View><Text>Open Menu</Text></View>
</MenuView>
// ❌ Wrong - no children
<MenuView menuItems={items} />MIT
Made with create-react-native-library

