Navigating through different screens is essential in mobile apps. In this guide, we’ll implement a custom tab navigation system in a React Native app using @react-navigation/bottom-tabs
. We’ll set up tab screens, create custom tab icons, and add animations for a smooth user experience.
Setting Up Tab Navigation
To get started, install the required navigation libraries:
yarn add @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack @react-navigation/bottom-tabs && cd ios && pod install && cd ..
Defining the Navigation Stack
Our Navigation
component will manage transitions between screens in the app. It’s the entry point of the app, where we define the main stack navigator.
Navigation.js
import HomeScreen from '@features/home/HomeScreen'
import IntroScreen from '@features/intro/IntroScreen'
import MainTabs from '@features/tabs/MainTabs'
import { NavigationContainer } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { navigationRef } from '@utils/NavigationUtils'
const Stack = createNativeStackNavigator()
const Navigation = () => {
return (
<NavigationContainer ref={navigationRef}>
<Stack.Navigator initialRouteName='IntroScreen' screenOptions={{ headerShown: false }}>
<Stack.Screen name='IntroScreen' component={IntroScreen} />
<Stack.Screen options={{ animation: 'fade' }} name='HomeScreen' component={HomeScreen} />
<Stack.Screen options={{ animation: 'fade' }} name='MainTabs' component={MainTabs} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default Navigation
In this stack navigator:
-
IntroScreen
: The introductory screen of the app. -
HomeScreen
: The main screen after logging in. -
MainTabs
: Contains the primary app tabs with custom navigation components.
Setting Up Tab Screens
Now let’s set up our main tabs, including custom icons and animations for smooth tab transitions.
MainTabs.js
import OrderScreen from '@features/order/OrderScreen'
import ProfileScreen from '@features/profile/ProfileScreen'
import ActivityScreen from '@features/activity/ActivityScreen'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
import CustomTabBar from './CustomTabBar'
const Tab = createBottomTabNavigator()
const MainTabs = () => {
return (
<Tab.Navigator
tabBar={(props) => <CustomTabBar {...props} />}
screenOptions={{
headerShown: false,
tabBarHideOnKeyboard: true
}}
>
<Tab.Screen name='Order' component={OrderScreen} />
<Tab.Screen name='Profile' component={ProfileScreen} />
<Tab.Screen name='Activity' component={ActivityScreen} />
</Tab.Navigator>
)
}
export default MainTabs
Here:
-
Order
,Profile
, andActivity
represent different screens for each tab. -
CustomTabBar
controls the tab bar’s appearance.
Customizing the Tab Bar
Creating a custom tab bar allows for personalized icons and animations. Here’s how we implement our custom tab bar:
CustomTabBar.js
import { View, TouchableOpacity, Image, Linking } from 'react-native'
import React from 'react'
import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated'
import { OrderTabIcon, ProfileTabIcon, ActivityTabIcon } from './TabIcon'
import { screenWidth } from '@utils/Constants'
const CustomTabBar = (props) => {
const { state, navigation } = props
// Animated style for sliding indicator
const indicatorStyle = useAnimatedStyle(() => {
const tabWidth = screenWidth / state.routes.length
return {
transform: [{ translateX: withTiming(state.index * tabWidth, { duration: 300 }) }],
width: tabWidth,
height: 3,
backgroundColor: 'blue',
}
})
return (
<View style={styles.tabBarContainer}>
<View style={styles.tabContainer}>
{state.routes.map((route, index) => {
const isFocused = state.index === index
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true
})
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name)
}
}
return (
<TouchableOpacity
key={index}
onPress={onPress}
style={[styles.tabItem, isFocused && styles.focusedTabItem]}
>
{route.name === 'Order' && <OrderTabIcon focused={isFocused} />}
{route.name === 'Profile' && <ProfileTabIcon focused={isFocused} />}
{route.name === 'Activity' && <ActivityTabIcon focused={isFocused} />}
</TouchableOpacity>
)
})}
</View>
{/* Sliding indicator */}
<Animated.View style={[styles.slidingIndicator, indicatorStyle]} />
</View>
)
}
export default CustomTabBar
In this component:
- Each tab button (
TouchableOpacity
) navigates to the corresponding screen on press. - The sliding indicator (
indicatorStyle
) animates horizontally, moving with each tab. - We also add an external link button for additional functionality.
Adding Custom Tab Icons
Custom icons give a unique look to each tab. Here’s how we define icons for each tab:
TabIcon.js
import OrderIcon from '@assets/icons/order.png'
import ProfileIcon from '@assets/icons/profile.png'
import ActivityIcon from '@assets/icons/activity.png'
import { Image } from 'react-native'
import React from 'react'
export const OrderTabIcon = ({ focused }) => (
<Image source={OrderIcon} style={{ tintColor: focused ? 'blue' : 'gray' }} />
)
export const ProfileTabIcon = ({ focused }) => (
<Image source={ProfileIcon} style={{ tintColor: focused ? 'blue' : 'gray' }} />
)
export const ActivityTabIcon = ({ focused }) => (
<Image source={ActivityIcon} style={{ tintColor: focused ? 'blue' : 'gray' }} />
)
Each icon changes color based on whether it is selected, providing visual feedback to the user.
Styles
Here’s a simple style setup for the custom tab bar and indicator.
styles.js
import { StyleSheet } from 'react-native'
export const styles = StyleSheet.create({
tabBarContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 10,
backgroundColor: 'white',
position: 'absolute',
bottom: 0,
width: '100%',
borderTopWidth: 1,
borderTopColor: '#ccc',
},
tabContainer: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
tabItem: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 10,
},
focusedTabItem: {
color: 'blue',
},
slidingIndicator: {
position: 'absolute',
bottom: 0,
},
externalLink: {
position: 'absolute',
right: 20,
bottom: 10,
},
icon: {
width: 24,
height: 24,
}
})
Conclusion
Using React Native’s navigation libraries, you can create a customized tab navigation system with animations, icons, and additional actions. By incorporating animations and unique icons, you create a polished user experience that enhances the app’s functionality and visual appeal.