React Native is an amazing tool for creating beautiful and high-performing mobile applications capable of running on both iOS and Android devices. When developing these apps, it's best to create navigation that allows users to move easily from one screen to the next. The React Navigation library does a fantastic job of providing various navigation patterns for things like stacks, tabs, and drawers that can be implemented and customized based on the UI design of your app.

In this post, we're going to create a custom tab bar using the React Navigation library bottom tabs component. We'll start by building a simple tab bar and then make it translucent using a blur view. Let's get started!

Prerequisites

To get the most out of this tutorial, you'll want to familiarize yourself with JavaScript/ES6 and meet the following requirements in your local dev environment:

  • Node.js version >= 12.x.x installed.
  • Have access to a package manager like npm, yarn, or npx.
  • Have react-native-cli installed, or use npx.

Installing react-navigation library

To create a new React Native app, execute the following command from a terminal window on your local dev environment. Next, navigate inside the project directory created by react-native-cli and install the following dependencies:

npx react-native init customTabBar

cd customTabBar

# install dependencies
yarn add @react-navigation/native @react-navigation/bottom-tabs react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view react-native-vector-icons @react-native-community/blur

Please note that to successfully implement this tutorial, you will need to use the React Navigation v5 library. After installing these dependencies, import the Gesture Handler library at the top of the index.js file of your React Native app:

import 'react-native-gesture-handler';

Then, for iOS, install the CocoaPods for all these dependencies by navigating inside the ios directory in a terminal window and executing the following command. If you do not have CocoaPods installed on your local dev machine, follow the alternate command below:

cd ios && pod install

# after pods are installed
cd ..

# alternate command
npx pod-install ios

Installing react-native-vector-icons

The react-native-vector-icons module needs a bit more configuration for iOS and Android platforms.

For iOS, you need to add the following inside ios/customTabBar/Info.plist:

<key>UIAppFonts</key>
<array>
  <string>AntDesign.ttf</string>
  <string>Entypo.ttf</string>
  <string>EvilIcons.ttf</string>
  <string>Feather.ttf</string>
  <string>FontAwesome.ttf</string>
  <string>FontAwesome5_Brands.ttf</string>
  <string>FontAwesome5_Regular.ttf</string>
  <string>FontAwesome5_Solid.ttf</string>
  <string>Foundation.ttf</string>
  <string>Ionicons.ttf</string>
  <string>MaterialIcons.ttf</string>
  <string>MaterialCommunityIcons.ttf</string>
  <string>SimpleLineIcons.ttf</string>
  <string>Octicons.ttf</string>
  <string>Zocial.ttf</string>
  <string>Fontisto.ttf</string>
</array>

Then, add the following to the ios/Podfile and run a cd ios && pod update from a terminal window:

pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'

For Android, add the following snippet in the file android/app/build.gradle:

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

And that's all you need to setup the react-native-vector-icons library!

Now that we have installed and configured everything required to build and run our app, we can now edit the App.js file—otherwise known as the entry point for the React Native app.

To build the app for iOS, execute the following command npx react-native run-ios from a terminal window. Similarly, the build command for Android is npx react-native run-android.

Here is the default app running after the build for iOS:

Add mockup screens

The tab bar we're building in this example app will display three different tabs. The first tab is used to display a list of items with images so that when the tab bar is added to the app, we can configure its translucency.

To start, let's add them inside a separate directory called screens/ and create the first file with the name, data.js. This file will contain the mockup data that will display inside the list view in the first tab. Next, add the following code snippet:

export const data = [
  {
    id: '1',
    title: 'Manarola, Italy',
    description: 'The Cliffs of Cinque Terre',
    image_url:
      'https://images.unsplash.com/photo-1516483638261-f4dbaf036963?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=633&q=80',
    iconName: 'location-pin',
  },

  {
    id: '2',
    title: 'Venezia, Italy',
    description: 'Rialto Bridge, Venezia, Italy',
    image_url:
      'https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=630&q=80',
    iconName: 'location-pin',
  },
  {
    id: '3',
    title: 'Prague, Czechia',
    description: 'Tram in Prague',
    image_url:
      'https://images.unsplash.com/photo-1513805959324-96eb66ca8713?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
    iconName: 'location-pin',
  },
  {
    id: '4',
    title: 'Venezia, Italy',
    description: 'Rialto Bridge, Venezia, Italy',
    image_url:
      'https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?ixlib=rb-1.2.1&ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&auto=format&fit=crop&w=630&q=80',
    iconName: 'location-pin',
  },
];

In the above code snippet, you can see that data is an array that has different objects.

Let's create the first tab screen and call it Home.js; this is where the array of mockup data will be used. Import the following statements inside it and then define a custom width and height for the image card. This image card is displayed inside the list view as an item. Using React Native's Dimensions API, the width and height of the image are calculated based on the width of the device's screen.

import React from 'react';
import {
  View,
  Text,
  StyleSheet,
  Image,
  Dimensions,
  ScrollView,
} from 'react-native';

import {data} from './data';

const {width} = Dimensions.get('screen');

const ITEM_WIDTH = width * 0.9;
const ITEM_HEIGHT = ITEM_WIDTH * 0.9;

Using a Text component, the title of the tab is displayed, and using the ScrollView, the list of items is implemented using JavaScript's map() method, which allows you to iterate over each item. After importing the statements, add the following snippet:

const Home = () => {
  const tabBarheight = useBottomTabBarHeight();
  return (
    <View style={styles.container}>
      <View style={styles.contentContainer}>
        <Text style={styles.title}>Home</Text>
      </View>

      {/* Scrollable Content */}
      <View style={styles.scrollContainer}>
        <ScrollView
          indicatorStyle="white"
          contentContainerStyle={[
            styles.scrollContentContainer,
            {paddingBottom: tabBarheight},
          ]}>
          {data.map((item) => (
            <View key={item.id} style={styles.imageContainer}>
              <Image
                style={styles.imageCard}
                source={{uri: item.image_url}}
                resizeMode="cover"
              />
            </View>
          ))}
        </ScrollView>
      </View>
    </View>
  );
};

export default Home;

Lastly, add the styles reference for each component in the above snippet:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0f0f0f',
  },
  contentContainer: {
    marginTop: 50,
    alignItems: 'center',
    paddingHorizontal: 20,
    paddingBottom: 20,
  },
  title: {
    fontSize: 20,
    color: '#fff',
  },
  scrollContainer: {
    flex: 1,
  },
  scrollContentContainer: {
    alignItems: 'center',
  },
  imageContainer: {
    marginBottom: 14,
  },
  imageCard: {
    borderRadius: 14,
    width: ITEM_WIDTH,
    height: ITEM_HEIGHT,
  },
});

The other two tab screens are created inside Browse.js and Library.js, and they do not render much information other than the name of the tab screen.

Inside the file Browse.js, add the following code snippet:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';

const Browse = () => {
  return (
    <View style={styles.container}>
      <View style={styles.contentContainer}>
        <Text style={styles.title}>Browse</Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0f0f0f',
  },
  contentContainer: {
    marginTop: 50,
    alignItems: 'center',
    paddingHorizontal: 20,
  },
  title: {
    fontSize: 20,
    color: '#fff',
  },
});

export default Browse;

Inside the Library.js file, add the following snippet:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';

const Library = () => {
  return (
    <View style={styles.container}>
      <View style={styles.contentContainer}>
        <Text style={styles.title}>Library</Text>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0f0f0f',
  },
  contentContainer: {
    marginTop: 50,
    alignItems: 'center',
    paddingHorizontal: 20,
  },
  title: {
    fontSize: 20,
    color: '#fff',
  },
});

export default Library;

Now that our mockup data is inside one of the tab screen components, we can move on from the tab screens to our tab bar.

How to create a tab bar

Create a new directory called navigation/ at the root of the React Native project. This is where we are going to store all of our navigation configuration files. Inside it, create a new directory called TabNavigator. This new directory will store two separate files:

  • index.js to initiate the complete Tab Bar configuration
  • CustomTabBar.js to render the custom tab bar

Inside the file TabNavigator/index.js, import the createBottomTabNavigator from the @react-navigation/bottom-tabs package. Using this, a Tab object is initialized. This object allows us to define the structure of the routes using the Tab.Navigator and each route using the Tab.Screen component.

import React from 'react';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

import Home from '../../screens/Home';
import Browse from '../../screens/Browse';
import Library from '../../screens/Library';

const Tab = createBottomTabNavigator();

const TabNavigator = () => {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Home" component={Home} />
      <Tab.Screen name="Browse" component={Browse} />
      <Tab.Screen name="Library" component={Library} />
    </Tab.Navigator>
  );
};

export default TabNavigator;

Now, our simple tab bar configuration is done! To see it in action, wrap it with the NavigationContainer component inside the new file called navigation/RootNavigator.js. This component manages the navigation tree, and it contains the navigation state prop.

import * as React from 'react';
import {NavigationContainer} from '@react-navigation/native';

import TabNavigator from './TabNavigator';

const RootNavigator = () => {
  return (
    <NavigationContainer>
      <TabNavigator />
    </NavigationContainer>
  );
};

export default RootNavigator;

The last step is to import and render the Root Navigator from inside the App.js file:

import React from 'react';
import {StatusBar} from 'react-native';

import RootNavigator from './navigation/RootNavigator';

const App = () => {
  return (
    <>
      <StatusBar hidden />
      <RootNavigator />
    </>
  );
};

export default App;

Using an iOS simulator, we can see the tab bar we've created below. At this point, we haven't added any custom styles to the tab bar, so it has a default style from the React Navigation library.

Add icons to the tab bar

To add icons to each tab, first import the Icon component from react-native-vector-icons library inside the navigation/TabNavigator/index.js file. For this example, let's use AntDesign based icons.

// after other import statements
import Icon from 'react-native-vector-icons/AntDesign';

Using the screenOptions object on Tab.Navigator, we can enable the configuration to display icons for each tab. This object has different methods and properties that allow us to enable various configurations. One such method is called tabBarIcon, which allows us to display a custom icon for each tab. This function returns an Icon component that has props like color and size to apply tint color on the icon for each tab and define a numeric value for the size of the icon. It also has a prop called name which allows us to define which icon should be used for which screen.

Add the following code snippet:

const screenOptions = (route, color) => {
  let iconName;

  switch (route.name) {
    case 'Home':
      iconName = 'home';
      break;
    case 'Browse':
      iconName = 'appstore-o';
      break;
    case 'Library':
      iconName = 'folder1';
      break;
    default:
      break;
  }

  return <Icon name={iconName} color={color} size={24} />;
};

const TabNavigator = () => {
  return (
    <Tab.Navigator
      screenOptions={({route}) => ({
        tabBarIcon: ({color}) => screenOptions(route, color),
      })}>
      {/* rest remains same */}
    </Tab.Navigator>
  );
};

If you return to the iOS simulator, you will notice that the icons for each tab route are now displayed.

Customizing the tab bar

To customize the tab bar, we'll start by applying a tabBarOptions object. This options object has properties that allow you to set both active and inactive tint color for each of the tabs and the background color for the whole tab bar.

Next, add the following options object on Tab.Navigator:

<Tab.Navigator
// ...
tabBarOptions={{
  activeTintColor: 'white',
  inactiveTintColor: '#d9d9d9',
  style: {
    borderTopColor: '#66666666',
    backgroundColor: 'transparent',
    elevation: 0,
  },
}}
>

The property elevation is set to zero in the above code snippet so that there is no shadow overlap on Android when we make the custom tab bar translucent in the next section.

Making the tab bar translucent

To make the tab bar translucent, we are going to use the BlurView component from @react-native-community/blur, and it is going to wrap a component called BottomTabBar from the @react-navigation/bottom-tabs library. This component is a React element that is used to display the actual tab bar, which is provided by a prop called tabBar on Tab.Navigator. Using this React element, the tab bar can be defined explicitly inside the CustomTabBar.js component file.

Start by adding the following snippet inside the TabNavigator/CustomTabBar.js file:

import React from 'react';
import {BottomTabBar} from '@react-navigation/bottom-tabs';
import {BlurView} from '@react-native-community/blur';

const CustomTabBar = (props) => {
  return <BottomTabBar {...props} />;
};

export default CustomTabBar;

The props received by this custom React component are passed from the tabBar option. Add it on the Tab.Navigator inside the TabNavigator/index.js file:

<Tab.Navigator
  // ... rest remains same
  tabBar={(props) => <CustomTabBar {...props} />}
>

Using the props, you can further modify the configuration of the bottom tab bar, but that's a topic for another day and another blog post.

Inside the CustomTabBar.js file, wrap the BottomTabBar with the <BlurView> component. Note that there is a different set of props for both iOS and Android in order to implement the blur view effect.

To create a blur view effect for iOS, add blurType, which accepts the type of blur effect as a string value. On iOS, different values for light, dark, xlight, and regular are available to customize the overall appearance of the tab. And another property, blurAmount, can be applied to adjust the intensity of the blur effect.

Similarly for Android, props like overLayColor can be used to create a custom overlay, and blurRadius can be used to manually adjust the blur effect radius.

To ensure that any styles applied are made on the BlurView component and that the tab bar is displayed on top of the content for each screen, set its position to absolute.

Here is the final snippet for the CustomTabBar component:

<BlurView
  style={{
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
  }}
  blurType="dark"
  blurAmount={10}
  blurRadius={25}
  overlayColor="transparent">
  <BottomTabBar {...props} />
</BlurView>

The tab bar is now translucent! Here is an example of our app component running on an iOS simulator.

In the above example, you'll notice that the BlurView component is set to absolute, but we still need to apply the paddingBottom property in the ScrollView component inside the Home.js tab screen.

The value of this property should be set to be the height of the whole tab bar. To get the height of the current tab bar, the @react-navigation/bottom-tabs module has a hook called useBottomTabBarHeight which provides this value.

From here, add the import statement in the screens/Home.js file and use the hook to get the height as shown below, applying it as the value of the paddingBottom style property in the ScrollView component:

import {useBottomTabBarHeight} from '@react-navigation/bottom-tabs';

// ...

const Home = () => {
  const tabBarheight = useBottomTabBarHeight();

  // ...

  return (
    // ...
    <ScrollView
      indicatorStyle="white"
      contentContainerStyle={[
        styles.scrollContentContainer,
        {paddingBottom: tabBarheight},
    ]}>
  )
}

Back in the iOS simulator, you will see that it works just fine now:

Running the app build on an Android simulator, the results are similar:

Conclusion

Although we only went through a single scenario for customizing the bottom tab bar in this blog post, the options for customization are endless. Familiarize yourself with the component-based configuration of the Tab Navigator in the latest version of the react-navigation library, try out a few different options, and see what works best with your branding!

If you're looking to build a scalable cross-platform app, Crowdbotics is here to help. Our expert PMs and developers can convert your website into a native app or vice-versa or build a brand new universal app from scratch. Get in touch with us today for a detailed quote and timeline!