Building an Instagram-Style Bottom Navigation in React Native(Expo)

Building an Instagram-Style Bottom Navigation in React Native(Expo)

Building a customized app navigator

ยท

6 min read

Hey there, lovely readers! ๐Ÿ‘‹ It's Cindy Kandie, and I'm back after a refreshing break in August. I know, I know, it wasn't exactly planned, but sometimes life throws you a curveball, right? But fret not, I'm back on track and ready to pick up where we left off with our weekly blog posts. Thanks for always being here!

Embracing My Fate as a Turnt React Native Developer

During my little hiatus, something amazing happened. I accepted my fate and embraced the world of React Native. And guess what? I even snagged a cool title for myself โ€“ Mobile Developer, thanks to React Native! ๐Ÿ˜„ Today, I'm super excited to share my journey of creating a bottom navigation bar similar to the one you see on Instagram.

Before we dive in, here's what you'll need:

  • Basic knowledge of React Native.

  • Familiarity with React.

  • A sprinkle of Tailwind CSS for styling.

Now, let's get started step by step.

Step 1: Setting Up Your Expo Project

First things first, let's set up our Expo project. You can follow the detailed instructions in the Expo documentation to get started.

Step 2: Installing NativeWind for TailWind

To make our styling life easier, we'll use NativeWind. You can install it by following the guide here.

Step 3: Explaining the Screens

Before we delve into the code, let's briefly discuss the screens you want to include in your bottom navigation. For this example, we have 'Home,' 'Search,' 'Category,' 'Notification,' and 'Profile' screens.

Step 4: Creating the Initial Views

We'll begin with a simple setup of views and no styling. Here's the code to get us started:
Note: To make things easier, I added an array so I can simply map over to display the tab names. I am also using expo/vector-icons, make sure you install these.

import { Text, View, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';

function BottomNav() {
  const tabItems = [
    { name: 'Home', icon: 'home' },
    { name: 'Search', icon: 'search' },
    { name: 'Category', icon: 'list' },
    { name: 'Notification', icon: 'notifications' },
    { name: 'Profile', icon: 'person' },
  ];

  return (
    <View>
      {tabItems.map((tab) => (
        <TouchableOpacity key={tab.name}>
          <Ionicons name={tab.icon} size={24} color="black" />
          <Text>{tab.name}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
}

export default BottomNav;

Step 5: Adding Styling

Now, let's make it visually appealing with Tailwind CSS. This is where the magic happens. Check out the code below:
Note: Some of the styling has been done using inline styling 'style:{{}}' let's just say that tailwind wasn't responding and gave me a mini-migraine. But all is well :)

import React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';

function BottomNav() {

  const tabItems = [
    { name: 'Home', icon: 'home' },
    { name: 'Search', icon: 'search' },
    { name: 'Category', icon: 'list' },
    { name: 'Notification', icon: 'notifications' },
    { name: 'Profile', icon: 'person' },
  ];
  return (
    <View
      className={`flex-row justify-between items-center bg-cyan-400 p-2 content-evenly bg-white gap-1 px-2 py-0 pb-3`}
      style={{
        position: 'absolute',
        bottom: 0,
        left: 0,
        right: 0,
        zIndex: 20,
      }}
    >
      {tabItems.map((tab) => (
        <TouchableOpacity
          key={tab.name}
          onPress={() => handleTabPress(tab.name)}
          className={`flex-1 items-center p-1`}
          style={{
            alignItems: 'center',
            borderTopWidth: activeRouteName === tab.name ? 2 : 0,
            borderColor: activeRouteName === tab.name ? 'cyan' : 'transparent',
          }}
        >
          <Ionicons
            name={tab.icon}
            size={24}
            color='black'}
          />
          <Text
            style={{
              fontSize: 12,
              paddingTop: 1,
              color:'black',
            }}
          >
            {tab.name}
          </Text>
        </TouchableOpacity>
      ))}
    </View>
  );
}

export default BottomNav;

Step 5: Adding the Almighty Functions

Now, let's add the functions that will power our bottom navigation.
- These functions tell the icons and tabs how to behave on click i.e. handling navigation, and which specific screens the entire is supposed to appear on.
Note: I have updated the code to work with these important functions correctly. I have added comments to clear any doubts.

import React, { useState, useContext } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useNavigationState, useNavigation } from '@react-navigation/native';

function BottomNav() {
  // Get the current navigation state and navigation object
  const navigationState = useNavigationState((state) => state);
  const navigation = useNavigation();

  // Determine the active route name
  const activeRouteName = navigationState?.routes[navigationState?.index]?.name;

  // State to keep track of the selected tab
  const [selectedTab, setSelectedTab] = useState(activeRouteName);

  // Function to handle tab press
  const handleTabPress = (tabName) => {
    setSelectedTab(tabName);
    navigation.navigate(tabName);
  };

  // Array of tab items, each with a name and icon
  const tabItems = [
    { name: 'Home', icon: 'home' },
    { name: 'Search', icon: 'search' },
    { name: 'Category', icon: 'list' },
    { name: 'Notification', icon: 'notifications' },
    { name: 'Profile', icon: 'person' },
  ];

  // Array of screens to show in the bottom navigation
  const screensToShowBottomNav = ['Home', 'Search', 'Category', 'Notification', 'Profile'];

  // If the active route is not in the screensToShowBottomNav array, don't render the bottom navigation
  if (!screensToShowBottomNav.includes(activeRouteName)) {
    return null;
  }

  return (
    <View
      // Add styling and class name based on your theme
      style={{
        position: 'absolute',
        bottom: 0,
        left: 0,
        right: 0,
        zIndex: 20,
      }}
    >
      {tabItems.map((tab) => (
        <TouchableOpacity
          key={tab.name}
          onPress={() => handleTabPress(tab.name)}
          // Add styling and class name based on your theme
          style={{
            alignItems: 'center',
            borderTopWidth: activeRouteName === tab.name ? 2 : 0,
            borderColor: activeRouteName === tab.name ? 'cyan' : 'transparent',
          }}
        >
          <Ionicons
            name={tab.icon}
            size={24}
            color={activeRouteName === tab.name ? 'cyan' : 'black'}
          />
          <Text
            style={{
              fontSize: 12,
              paddingTop: 1,
              color: activeRouteName === tab.name ? 'cyan' : 'black',
            }}
          >
            {tab.name}
          </Text>
        </TouchableOpacity>
      ))}
    </View>
  );
}

export default BottomNav;

Note: The tab items array can have another attribute. If you want to customize the tab names appearing on the UI, you can add a 'handle' (for example) attribute in the tab item object and use it for naming the tab other than the actual screen name.

const tabItems = [ 
    { handle: 'Feed', name: 'Home', icon: 'home' }, 
    { handle: 'Search', name: 'Search', icon: 'search' }, 
    { handle: 'Categories', name: 'Category', icon: 'list' }, 
    { handle: 'Notifications', name: 'Notification', icon: 'notifications' }, 
    { handle: 'User', name: 'Profile', icon: 'person' },
     // You can add more tab items with custom names if needed. 
];


        //on the tab name section
          <Text
            style={{
              fontSize: 12,
              paddingTop: 1,
              color: activeRouteName === tab.name ? 'cyan' : theme.textColor,
            }}
          >
            {tab.handle}
          </Text>

Step 7: Placing the Component

To complete the process, make sure to include your BottomNav component in your App.js (or equivalent) to ensure it's rendered throughout your app.


export default function App() {
return (
   <NavigationContainer>
      <StatusBar style="light" />
      <AuthContextProvider>
        <Root /> //Probably with the main stack navigator
        <BottomNav/>
      </AuthContextProvider>
    </NavigationContainer>
);
}

And there you have it, folks! You've just created an Instagram-style bottom navigation bar for your React Native app ๐Ÿš€ Feel free to customize it further to match your app's unique style.

What's Next?

In our next adventure(and I mean blog) together, we'll dive deeper into React Native and explore more cool features and tricks to level up your mobile app development game.

Stay tuned for that! And more thanks for being here and learning with me.
Until then, happy coding, and may your Development journey be as fun as mine.

ย