logoChisa UI

Command Palette

Search for a command to run...

Componentschevron_rightBottomNavigation

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 */}
<BottomNavigation
items={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';
4
5const { width: SCREEN_WIDTH } = Dimensions.get('window');
6
7type BottomNavVariant = 'default' | 'floating' | 'minimal' | 'pill';
8
9interface NavItem {
10 key: string;
11 label: string;
12 icon: React.ReactNode;
13}
14
15interface 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}
25
26export 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;
32
33 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 };
40
41 return (
42 <View style={getContainerStyle()}>
43 <View style={styles.itemsContainer}>
44 {items.map((item) => (
45 <TouchableOpacity
46 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};
64
65const 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});
73
74export default BottomNavigation;

Props

NameTypeDefaultDescription
itemsNavItem[]-Navigation items with key, label, icon
activeKeystring-Currently active item key
onSelect(key: string) => void-Callback when item is pressed
variant'default' | 'floating' | 'minimal' | 'pill''default'Visual variant
durationnumber200Animation duration in ms
activeColorstring'#E16C2D'Active/primary color
inactiveColorstring'#9CA3AF'Inactive color
showLabelsbooleantrueShow text labels