Native iOS context menus with haptic feedback, SF Symbols, and interactive emoji reactions for React Native. Provides an elegant focus menu UI component for Expo and React Native apps.
- π± Native iOS Context Menus - Uses UIContextMenuInteraction for authentic iOS experience
- π― Focus Menu UI - Long press or tap to reveal contextual actions
- π« Haptic Feedback - Configurable haptic response on menu activation
- π¨ SF Symbols Support - Use any SF Symbol as menu item icons
- π Emoji Reactions - Interactive emoji picker for quick reactions
- π Customizable Triggers - Long press or tap activation modes
- π¦ Submenus - Nested menu items for complex hierarchies
- π΄ Destructive Actions - Native styling for dangerous operations
- βΏ Accessibility - Full VoiceOver and accessibility support
expo install expo-focus-menuFirst ensure you have installed and configured the expo package.
npm install expo-focus-menu
# or
yarn add expo-focus-menuRun pod install after installation:
cd ios && pod installNote: This module currently only supports iOS. Android support displays a fallback view.
import { ExpoFocusMenuView } from 'expo-focus-menu';
function MyComponent() {
const menuItems = [
{ id: 'share', title: 'Share', icon: 'square.and.arrow.up' },
{ id: 'copy', title: 'Copy', icon: 'doc.on.doc' },
{ id: 'delete', title: 'Delete', icon: 'trash', destructive: true }
];
return (
<ExpoFocusMenuView
items={menuItems}
onItemPress={(itemId) => console.log('Selected:', itemId)}
>
<Text>Long press me!</Text>
</ExpoFocusMenuView>
);
}<ExpoFocusMenuView
items={menuItems}
reactions={['π', 'β€οΈ', 'π', 'π₯', 'π―']}
onReactionPress={({ emoji, selected }) => {
console.log(`Emoji ${emoji} was ${selected ? 'selected' : 'deselected'}`);
}}
onItemPress={(itemId) => console.log('Menu item:', itemId)}
>
<View style={styles.card}>
<Text>React to this content!</Text>
</View>
</ExpoFocusMenuView>const menuItems = [
{ id: 'edit', title: 'Edit', icon: 'pencil' },
{
id: 'share',
title: 'Share',
icon: 'square.and.arrow.up',
children: [
{ id: 'twitter', title: 'Twitter', icon: 'bird' },
{ id: 'facebook', title: 'Facebook', icon: 'f.circle' },
{ id: 'email', title: 'Email', icon: 'envelope' }
]
},
{ id: 'delete', title: 'Delete', icon: 'trash', destructive: true }
];<ExpoFocusMenuView
items={menuItems}
hapticFeedback={true} // Enable haptic feedback
onItemPress={handleItemPress}
onMenuShow={() => console.log('Menu opened')}
onMenuDismiss={() => console.log('Menu closed')}
>
<YourContent />
</ExpoFocusMenuView>| Prop | Type | Description | Default |
|---|---|---|---|
items |
FocusMenuItem[] |
Array of menu items to display | Required |
onItemPress |
(itemId: string) => void |
Callback when menu item is selected | Required |
children |
ReactNode |
Content to wrap with menu | Required |
hapticFeedback |
boolean |
Enable haptic feedback | false |
reactions |
string[] |
Emoji reactions to display (none if omitted) | - |
onReactionPress |
(data: {emoji: string, selected: boolean}) => void |
Reaction selection callback | - |
onMenuShow |
() => void |
Menu shown callback | - |
onMenuDismiss |
() => void |
Menu dismissed callback | - |
interface FocusMenuItem {
id: string; // Unique identifier
title: string; // Display title
subtitle?: string; // Optional subtitle (iOS 15+)
icon?: string; // SF Symbol name
image?: string; // Custom image URL or base64
destructive?: boolean; // Style as destructive action
disabled?: boolean; // Disable this item
children?: FocusMenuItem[]; // Nested submenu items
}| Platform | Status | Notes |
|---|---|---|
| iOS 13+ | β Fully supported | Native UIContextMenuInteraction |
| iOS 14+ | β Enhanced | UIMenu with advanced features |
| iOS 15+ | β Enhanced | Subtitles support |
| Android | Displays children without menu | |
| Web | Displays children without menu |
| expo-focus-menu | Expo SDK | React Native | iOS | Android | Node |
|---|---|---|---|---|---|
| 0.1.x | 54+ | 0.81+ | 15.0+ | API 24+ (SDK 36) | 20.0+ |
| Dependency | Version | Required |
|---|---|---|
| expo | * | Yes (peer) |
| react | * | Yes (peer) |
| react-native | * | Yes (peer) |
| ExpoModulesCore | Auto-linked | Yes (iOS) |
| Configuration | iOS | Android |
|---|---|---|
| Swift Version | 5.9 | - |
| Compile SDK | - | 36 |
| Min SDK | iOS 15.0, tvOS 15.0 | API 24 |
| Target SDK | - | 36 |
Check the example directory for a complete working example with various use cases.
# Run the example app
cd example
npm install
npm run ios# Run all tests
npm test
# Run iOS specific tests
npm run test:ios
# Run with coverage
npm test -- --coverageContributions are welcome! Please read our Contributing Guide for details on our code of conduct and the process for submitting pull requests.
- Android support with native implementation
- Web context menu support
- Custom menu animations
- Menu item badges
- Dynamic menu updates
- Custom preview views
- Menu section headers
MIT Β© shottah
- Built with Expo Modules API
- Inspired by iOS native context menu interactions
- Emoji picker design inspired by popular messaging apps
- π Report bugs
- π‘ Request features
- π Read the docs
- β Star this repo if you find it useful!