BottomNavigation
Bottom navigation bar with 4 variants (default, floating, minimal, pill) and smooth animations.
9:41
signal_cellular_altwifibattery_full
Default
home
Floating
home
Minimal
home
Pill
home
Installation
$
Usage
Example.tsx
import { View, Text } from 'react-native';import { BottomNavigation } from './components/BottomNavigation';import { Ionicons } from '@expo/vector-icons';const navItems = [{ key: 'home', label: 'Home', icon: <Ionicons name="home-outline" size={24} /> },{ key: 'search', label: 'Search', icon: <Ionicons name="search-outline" size={24} /> },{ key: 'profile', label: 'Profile', icon: <Ionicons name="person-outline" size={24} /> },];export default function App() {const [active, setActive] = useState('home');return (<View style={{ flex: 1 }}>{/* Your content */}<BottomNavigationitems={navItems}activeKey={active}onSelect={setActive}variant="floating"activeColor="#E16C2D"/></View>);}
Full Component Code
BottomNavigation.tsx
1import React from 'react';2import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native';3import Animated, { useSharedValue, useAnimatedStyle, withTiming, withSpring, interpolate, Easing } from 'react-native-reanimated';45const { width: SCREEN_WIDTH } = Dimensions.get('window');67type BottomNavVariant = 'default' | 'floating' | 'minimal' | 'pill';89interface NavItem {10 key: string;11 label: string;12 icon: React.ReactNode;13}1415interface BottomNavigationProps {16 items: NavItem[];17 activeKey: string;18 onSelect: (key: string) => void;19 variant?: BottomNavVariant;20 duration?: number;21 activeColor?: string;22 inactiveColor?: string;23 showLabels?: boolean;24}2526export const BottomNavigation: React.FC<BottomNavigationProps> = ({27 items, activeKey, onSelect,28 variant = 'default', duration = 200,29 activeColor = '#E16C2D', inactiveColor = '#9CA3AF', showLabels = true,30}) => {31 const itemWidth = (variant === 'floating' ? SCREEN_WIDTH - 48 : SCREEN_WIDTH) / items.length;3233 const getContainerStyle = () => {34 switch (variant) {35 case 'floating': return [styles.container, styles.floatingContainer];36 case 'minimal': return [styles.container, styles.minimalContainer];37 default: return styles.container;38 }39 };4041 return (42 <View style={getContainerStyle()}>43 <View style={styles.itemsContainer}>44 {items.map((item) => (45 <TouchableOpacity46 key={item.key}47 onPress={() => onSelect(item.key)}48 style={[styles.navItem, { width: itemWidth }]}49 >50 {React.cloneElement(item.icon as React.ReactElement, {51 color: item.key === activeKey ? activeColor : inactiveColor,52 })}53 {showLabels && (54 <Text style={[styles.label, { color: item.key === activeKey ? activeColor : inactiveColor }]}>55 {item.label}56 </Text>57 )}58 </TouchableOpacity>59 ))}60 </View>61 </View>62 );63};6465const styles = StyleSheet.create({66 container: { width: '100%', paddingBottom: 20, paddingTop: 8, borderTopWidth: 1, borderTopColor: '#E5E5E5', backgroundColor: '#FFF' },67 floatingContainer: { position: 'absolute', bottom: 24, left: 24, right: 24, borderRadius: 24, borderTopWidth: 0, elevation: 8 },68 minimalContainer: { paddingVertical: 12, borderTopWidth: 0 },69 itemsContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around' },70 navItem: { alignItems: 'center', justifyContent: 'center', paddingVertical: 8 },71 label: { fontSize: 11, fontWeight: '600', marginTop: 4 },72});7374export default BottomNavigation;
Props
| Name | Type | Default | Description |
|---|---|---|---|
| items | NavItem[] | - | Navigation items with key, label, icon |
| activeKey | string | - | Currently active item key |
| onSelect | (key: string) => void | - | Callback when item is pressed |
| variant | 'default' | 'floating' | 'minimal' | 'pill' | 'default' | Visual variant |
| duration | number | 200 | Animation duration in ms |
| activeColor | string | '#E16C2D' | Active/primary color |
| inactiveColor | string | '#9CA3AF' | Inactive color |
| showLabels | boolean | true | Show text labels |
