DEV Community

Cover image for How to Integrate Stack and Bottom Tab Navigator in React Native
Amit Kumar
Amit Kumar

Posted on • Edited on

How to Integrate Stack and Bottom Tab Navigator in React Native

Navigating between screens is an essential part of any mobile application. In this blog, I'll guide you step-by-step on how to integrate Stack Navigator and Bottom Tab Navigator into your React Native project using @react-navigation.


Image description

Prerequisites

Before we begin, ensure you have the following setup:

  1. Create a React Native project using npx @react-native-community/cli init projectName.
  2. Follow the React Navigation Docs to set up the required dependencies. For this guide, use the following packages:
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/elements": "^2.2.5",
"@react-navigation/native": "^7.0.14",
"@react-navigation/native-stack": "^7.2.0",
"@react-navigation/stack": "^7.1.1",
"react": "18.3.1",
"react-native": "0.76.6",
"react-native-gesture-handler": "^2.22.0",
"react-native-safe-area-context": "^5.1.0",
"react-native-screens": "^4.5.0",

Enter fullscreen mode Exit fullscreen mode

Project Structure

Organize your project in the following way for better readability:

src/
├── screens/
│   ├── HomeScreen.js
│   ├── ProfileScreen.js
│   ├── DetailScreen.js
│   ├── SettingScreen.js
├── navigation/
│   ├── bottomNavigator.js
│   ├── rootNavigator.js
│   ├── styles.js
│   ├── navigators/
│       ├── detailStackNavigator.js
│       ├── homeStackNavigator.js
│       ├── profileStackNavigator.js
│       ├── settingStackNavigator.js

Enter fullscreen mode Exit fullscreen mode

Screenshot for the reference

Image description


Step 1: Setup App.js

In App.js, wrap your main navigator with the SafeAreaProvider for better handling of device-specific safe areas.

import React from 'react';
import MainNavigator from './src/navigation/rootNavigator';
import { SafeAreaProvider } from 'react-native-safe-area-context';

const App = () => {
  return (
    <SafeAreaProvider>
      <MainNavigator />
    </SafeAreaProvider>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

Step 2: Create Screens

Inside src/screens, create the following components:

Example: HomeScreen.js

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

const HomeScreen = () => {
  return (
    <View style={styles.container}>
      <Text>Home Screen</Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default HomeScreen;

Enter fullscreen mode Exit fullscreen mode

Repeat this process for ProfileScreen, DetailScreen, and SettingScreen, changing the displayed text accordingly.


Step 3: Create the Bottom Tab Navigator

In src/navigation/bottomNavigator.js, set up the Bottom Tab Navigator with custom icons for each tab.

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

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

import styles from './styles';
import {
  DetailAltIcon,
  DetailIcon,
  HomeAltIcon,
  HomeIcon,
  ProfileAltIcon,
  ProfileIcon,
  SettingAltIcon,
  SettingIcon,
} from '../assets';
import HomeStack from './navigators/homeStackNavigator';
import DetailStack from './navigators/detailStackNavigator';
import SettingStack from './navigators/settingStackNavigator';
import ProfileStack from './navigators/profileStackNavigator';

const BottomNavigator = () => {
  let WIDTH = 25;
  let HEIGHT = 25;

  const BottomTabNavigator = createBottomTabNavigator();

  const tabBarListeners = ({navigation, route}) => ({
    tabPress: () => navigation.navigate(route.name),
  });

  return (
    <BottomTabNavigator.Navigator
      tabBar={props => (
        <View style={styles.wrapperStyle}>
          <BottomTabBar {...props} style={styles.bottomTabStyles} />
        </View>
      )}
      screenOptions={{
        tabBarActiveTintColor: '#000000',
        tabBarInactiveTintColor: '#767676',
        tabBarStyle:{
          backgroundColor: '#fff',
          borderTopWidth: 0,
          elevation: 0,
          paddingTop: 10,
          height: Platform.OS === 'android' ? 65 : null,
        },
        tabBarLabelStyle: {
          fontFamily: "Arial",
          fontSize: 11,
        },
      }}>
      <BottomTabNavigator.Screen
        options={{
          tabBarShowLabel: true,
          gestureEnabled: false,
          unmountOnBlur: true,
          headerShown: false,
          tabBarIcon: ({focused}) =>
            focused ? (
              <HomeAltIcon width={WIDTH} height={HEIGHT} />
            ) : (
              <HomeIcon width={WIDTH} height={HEIGHT} />
            ),
        }}
        name="Home"
        listeners={tabBarListeners}
        component={HomeStack}
      />
      <BottomTabNavigator.Screen
        options={{
          tabBarShowLabel: true,
          gestureEnabled: false,
          unmountOnBlur: true,
          headerShown: false,
          tabBarIcon: ({focused}) =>
            focused ? (
              <SettingAltIcon width={WIDTH} height={HEIGHT} />
            ) : (
              <SettingIcon width={WIDTH} height={HEIGHT} />
            ),
        }}
        name="Setting"
        listeners={tabBarListeners}
        component={SettingStack}
      />
      <BottomTabNavigator.Screen
        options={{
          tabBarShowLabel: true,
          gestureEnabled: false,
          unmountOnBlur: true,
          headerShown: false,
          tabBarIcon: ({focused}) =>
            focused ? (
              <DetailAltIcon width={WIDTH} height={HEIGHT} />
            ) : (
              <DetailIcon width={WIDTH} height={HEIGHT} />
            ),
        }}
        name="Detail"
        listeners={tabBarListeners}
        component={DetailStack}
      />
      <BottomTabNavigator.Screen
        options={{
          tabBarShowLabel: true,
          gestureEnabled: false,
          unmountOnBlur: true,
          headerShown: false,
          tabBarIcon: ({focused}) =>
            focused ? (
              <ProfileAltIcon width={WIDTH} height={HEIGHT} />
            ) : (
              <ProfileIcon width={WIDTH} height={HEIGHT} />
            ),
        }}
        name="Profile"
        listeners={tabBarListeners}
        component={ProfileStack}
      />
    </BottomTabNavigator.Navigator>
  );
};

export default BottomNavigator;

Enter fullscreen mode Exit fullscreen mode

You can customize the tab bar further, such as adding icons, custom styles, or listeners.


Step 4: Create the Root Navigator

In src/navigation/rootNavigator.js, create the Stack Navigator and integrate the Bottom Tab Navigator.

import React from 'react';

import {NavigationContainer} from '@react-navigation/native';
import {
  CardStyleInterpolators,
  createStackNavigator,
} from '@react-navigation/stack';
import BottomNavigator from './bottomNavigator';
import SplashScreen from '../screens/SplashScreen';
import StackFullScreen from '../screens/StackFullScreen';

const Stack = createStackNavigator();

const LoginStack = () => {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Stack.Screen name="Splash" component={SplashScreen} />
      <Stack.Screen name="FullScreen" component={StackFullScreen} />
      <Stack.Screen
        name="HomeScreen"
        component={BottomNavigator}
        options={{
          cardStyleInterpolator: CardStyleInterpolators.forFadeFromCenter,
          gestureEnabled: false,
        }}
      />
    </Stack.Navigator>
  );
};

const MainNavigator = () => {
  return (
    <NavigationContainer
      options={{
        gestureEnabled: false,
      }}>
      <LoginStack />
    </NavigationContainer>
  );
};

export default MainNavigator;

Enter fullscreen mode Exit fullscreen mode

Step 5: Style the Tab Bar

In src/navigation/styles.js, define styles for the bottom tab bar.

import { StyleSheet } from 'react-native';

export default StyleSheet.create({
  wrapperStyle: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    // borderTopLeftRadius: 15,
    // borderTopRightRadius: 15,
    overflow: 'hidden',
  },
  bottomTabStyles: {
    // borderTopLeftRadius: 15,
    // borderTopRightRadius: 15,
  },
});
Enter fullscreen mode Exit fullscreen mode

Step 6: Organizing the Navigation Folder

To maintain a clean and scalable codebase, it's essential to organize your navigation files properly. We'll create a navigators folder within the navigation directory and define stack navigators for the following sections of the app:

  1. detailStackNavigator.js
  2. homeStackNavigator.js
  3. profileStackNavigator.js
  4. settingStackNavigator.js

Folder Structure

Here’s how the project structure looks after this step:

src/
├── screens/
│   ├── HomeScreen.js
│   ├── ProfileScreen.js
│   ├── DetailScreen.js
│   ├── SettingScreen.js
├── navigation/
│   ├── bottomNavigator.js
│   ├── rootNavigator.js
│   ├── styles.js
│   ├── navigators/
│       ├── detailStackNavigator.js
│       ├── homeStackNavigator.js
│       ├── profileStackNavigator.js
│       ├── settingStackNavigator.js

Enter fullscreen mode Exit fullscreen mode

Creating Stack Navigators

Inside the navigators folder, we will define the stack navigators for each section of the app.

1. Detail Stack Navigator

The detailStackNavigator.js file manages the stack for the detail section of the app. This includes the DetailScreen, which serves as the initial screen for this stack.

import React from 'react';
import {
  CardStyleInterpolators,
  createStackNavigator,
} from '@react-navigation/stack';

import DetailScreen from '../../screens/DetailScreen';
import WithInBottomScreen from '../../screens/WithInBottomScreen';

const Stack = createStackNavigator();

export default function DetailStack() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Stack.Screen
        name="DetailPage"
        component={DetailScreen}
        options={{
          cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
          gestureEnabled: false,
        }}
      />

      <Stack.Screen name="WithInBottomScreen" component={WithInBottomScreen} />
    </Stack.Navigator>
  );
}

Enter fullscreen mode Exit fullscreen mode

2. Home Stack Navigator

The homeStackNavigator.js is responsible for the main app flow, starting with the HomeScreen.

import React from 'react';
import {
  CardStyleInterpolators,
  createStackNavigator,
} from '@react-navigation/stack';

import HomeScreen from '../../screens/HomeScreen';
import WithInBottomScreen from '../../screens/WithInBottomScreen';

const Stack = createStackNavigator();

export default function HomeStack() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Stack.Screen
        name="HomePage"
        component={HomeScreen}
        options={{
          cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
          gestureEnabled: false,
        }}
      />

    <Stack.Screen name="WithInBottomScreen" component={WithInBottomScreen} />
    </Stack.Navigator>
  );
}


Enter fullscreen mode Exit fullscreen mode

3. Profile Stack Navigator

The profileStackNavigator.js organizes the profile-related screens, starting with ProfileScreen.

import React from 'react';
import {
  CardStyleInterpolators,
  createStackNavigator,
} from '@react-navigation/stack';

import ProfileScreen from '../../screens/ProfileScreen';
import WithInBottomScreen from '../../screens/WithInBottomScreen';

const Stack = createStackNavigator();

export default function ProfileStack() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Stack.Screen
        name="ProfilePage"
        component={ProfileScreen}
        options={{
          cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
          gestureEnabled: false,
        }}
      />

      <Stack.Screen name="WithInBottomScreen" component={WithInBottomScreen} />
    </Stack.Navigator>
  );
}

Enter fullscreen mode Exit fullscreen mode

4. Setting Stack Navigator

The settingStackNavigator.js defines the stack for the app's settings, starting with the SettingScreen.

import React from 'react';
import {
  CardStyleInterpolators,
  createStackNavigator,
} from '@react-navigation/stack';

import SettingScreen from '../../screens/SettingScreen';
import WithInBottomScreen from '../../screens/WithInBottomScreen';

const Stack = createStackNavigator();

export default function SettingStack() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerShown: false,
      }}>
      <Stack.Screen
        name="SettingPage"
        component={SettingScreen}
        options={{
          cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS,
          gestureEnabled: false,
        }}
      />

      <Stack.Screen name="WithInBottomScreen" component={WithInBottomScreen} />
    </Stack.Navigator>
  );
}

Enter fullscreen mode Exit fullscreen mode

Why Use Separate Navigators?

  1. Modularity: Each navigator focuses on a specific section of the app, making the code easier to manage and debug.
  2. Scalability: Adding more screens or functionality to a specific section becomes straightforward.
  3. Reusability: Stack navigators can be combined into other navigators (e.g., bottom tab or root navigators) without redundancy.

Handling Special Cases: Hiding the Bottom Tab Bar

Certain screens, like a full-screen modal or a detail view, may require hiding the bottom tab bar when displayed. Here's an example of how to achieve this.

WithInBottomScreen

The WithInBottomScreen is an example of a screen where the bottom tab bar is hidden when the user navigates to it. The tab bar is restored when the user navigates away.

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

const WithInBottomScreen = ({navigation}) => {
  useEffect(() => {
    // Hide the bottom tab bar when this screen is active
    navigation.getParent()?.setOptions({tabBarStyle: {display: 'none'}});
    return () => {
      // Restore the bottom tab bar when leaving this screen
      navigation.getParent()?.setOptions({
        tabBarStyle: {
          backgroundColor: '#fff',
          borderTopWidth: 0,
          elevation: 0,
          paddingTop: 10,
          height: Platform.OS === 'android' ? 65 : null,
        },
      });
    };
  }, [navigation]);

  return (
    <View style={styles.container}>
      <Text>WithInBottomScreen</Text>
      <Button title="Go Back" onPress={() => navigation.goBack()} />
    </View>
  );
};

export default WithInBottomScreen;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

Enter fullscreen mode Exit fullscreen mode

Running the App

  • Start your app using:
npx react-native start
npx react-native run-android # for Android
npx react-native run-ios     # for iOS

Enter fullscreen mode Exit fullscreen mode
  • You should now see a fully functional app with a Bottom Tab Navigator and a Stack Navigator integrated.

Part 2

How to Integrate Stack, Bottom Tab, and Drawer Navigator in React Native


Conclusion

Integrating Stack and Bottom Tab Navigators allows you to create complex navigation flows in your React Native app. This guide provides the foundation, and you can build upon it by adding more screens, customizing navigation, or using advanced features like deep linking or dynamic routes.


After reading the post consider the following:

  • Subscribe to receive newsletters with the latest blog posts
  • Download the source code for this post from my github

Let me know if you have any questions or need further assistance! 🚀

Top comments (0)