/

BlurMenu Component

A beautifully animated, blur-background menu component for React Native with Expo. Features smooth spring animations, customizable positioning, and a sophisticated modal management system.

Features

  • Smooth Animations: Spring-based animations with staggered menu item entrances
  • 🎯 Flexible Positioning: Support for 9 different FAB positions (top, bottom, center, left, right combinations)
  • 🌊 Blur Background: Elegant blur effect using Expo's BlurView
  • 🎭 Modal Management: Global modal manager ensures only one menu is active at a time
  • 🎨 Customizable: Support for custom icons, titles, and styling
  • 📱 Safe Area Aware: Automatically handles device safe areas
  • 🎪 Custom Triggers: Use your own component as the menu trigger

Dependencies

This component requires the following packages:

npm install expo-blur react-native-reanimated react-native-safe-area-context @expo/vector-icons

Basic Usage

import BlurMenu, { MenuItem } from '@/components/BlurMenu';
import { useState } from 'react';

const menuItems: MenuItem[] = [
  {
    id: 'profile',
    icon: 'person-outline',
    title: 'Profile',
    onPress: () => console.log('Profile pressed')
  },
  {
    id: 'settings',
    icon: 'settings-outline', 
    title: 'Settings',
    onPress: () => console.log('Settings pressed')
  },
  {
    id: 'logout',
    icon: 'log-out-outline',
    title: 'Logout',
    onPress: () => console.log('Logout pressed')
  }
];

export default function MyScreen() {
  const [menuVisible, setMenuVisible] = useState(false);

  return (
    <BlurMenu
      visible={menuVisible}
      onToggle={() => setMenuVisible(!menuVisible)}
      menuItems={menuItems}
      title="Main Menu"
      fabPosition="bottom-right"
    />
  );
}

Props

PropTypeDefaultDescription
visibleboolean-Required. Controls menu visibility
onToggle() => void-Required. Callback when menu should toggle
menuItemsMenuItem[]-Required. Array of menu items
positionMenuPosition'center'Menu content alignment ('left' | 'right' | 'center')
titlestring'Menu'Menu title displayed at the top
fabPositionFabPosition'bottom-center'FAB trigger position
childrenReact.ReactNode-Custom trigger component (replaces default FAB)
classNamestring-Additional CSS class for custom trigger

MenuItem Interface

interface MenuItem {
  id?: string;                    // Optional unique identifier
  icon?: React.ComponentType<any> // Icon component, React node, or Ionicons name
       | React.ReactNode 
       | string;
  title: string;                  // Menu item label
  onPress?: () => void;          // Callback when item is pressed
}

Position Types

type MenuPosition = 'left' | 'right' | 'center';

type FabPosition = 
  | 'top-left' | 'top-right' | 'top-center'
  | 'bottom-left' | 'bottom-right' | 'bottom-center'
  | 'left' | 'right' | 'center';

Advanced Usage Examples

Custom Trigger Component

<BlurMenu
  visible={menuVisible}
  onToggle={() => setMenuVisible(!menuVisible)}
  menuItems={menuItems}
  title="Actions"
>
  <View style={styles.customTrigger}>
    <Ionicons name="ellipsis-horizontal" size={24} color="white" />
  </View>
</BlurMenu>

Different FAB Positions

// Top-right corner menu
<BlurMenu
  visible={menuVisible}
  onToggle={() => setMenuVisible(!menuVisible)}
  menuItems={menuItems}
  fabPosition="top-right"
  title="Quick Actions"
/>

// Left-side menu  
<BlurMenu
  visible={menuVisible}
  onToggle={() => setMenuVisible(!menuVisible)}
  menuItems={menuItems}
  fabPosition="left"
  position="left"
  title="Navigation"
/>

Custom Icons

const menuItems: MenuItem[] = [
  {
    icon: <MyCustomIcon size={20} color="white" />,
    title: 'Custom Action',
    onPress: () => handleCustomAction()
  },
  {
    icon: () => <AnotherIcon />,
    title: 'Function Icon',
    onPress: () => handleAnotherAction()
  }
];

Menu with Context Actions

const contextMenuItems: MenuItem[] = [
  {
    icon: 'copy-outline',
    title: 'Copy',
    onPress: () => handleCopy()
  },
  {
    icon: 'share-outline', 
    title: 'Share',
    onPress: () => handleShare()
  },
  {
    icon: 'trash-outline',
    title: 'Delete',
    onPress: () => handleDelete()
  }
];

// Triggered by long press on an item
<BlurMenu
  visible={contextMenuVisible}
  onToggle={() => setContextMenuVisible(!contextMenuVisible)}
  menuItems={contextMenuItems}
  title="Actions"
  fabPosition="center"
/>

Animation Details

The component features sophisticated animations:

  • Spring Animations: Uses react-native-reanimated with optimized spring configurations
  • Staggered Entrance: Menu items appear with a 50ms delay between each item
  • Direction-Aware: Animation direction depends on FAB position
  • Smooth Transitions: 200-300ms timing for opacity and blur effects

Modal Management

The component includes a global modal manager that:

  • Ensures only one BlurMenu is active at a time
  • Automatically closes other menus when a new one opens
  • Handles proper cleanup when components unmount
  • Prevents modal conflicts in complex navigation scenarios

Styling Notes

  • Menu uses a dark blur background with white text
  • Menu items have subtle bottom borders with transparency
  • FAB has a subtle shadow for depth
  • All animations respect device safe areas
  • Responsive design adapts to different screen sizes

Best Practices

  1. Menu Items: Keep menu items concise (5-7 items maximum)
  2. Icons: Use consistent icon styling across menu items
  3. Position: Choose FAB position based on your app's navigation patterns
  4. Performance: Menu items are optimized with proper key props
  5. Accessibility: Consider adding accessibility labels for better UX

Troubleshooting

Menu not appearing: Ensure expo-blur is properly installed and linked.

Animations stuttering: Check that react-native-reanimated is configured correctly in your project.

Multiple menus conflict: The modal manager should handle this automatically, but ensure each BlurMenu has unique menu items.

License

This component is part of the landing-rn-components library.