Learn React Native

Using Nested Navigators

Stack and Tab Navigators

What is a Stack Navigator?

A Stack Navigator provides a way for your app to transition between screens where each new screen is placed on top of a stack.

What is a Tab Navigator?

A Tab Navigator is a simple tab bar at the bottom of the screen that lets you switch between different routes. Routes are lazily initialized -- their screen components are not mounted until they are first focused.

In this article we will learn to use nested navigators in react native by placing a stack navigator inside a tab navigator.

Setting Up

yarn add @react-navigation/native @react-navigation/bottom-tabs @react-navigation/stack

We want to create a navigation structure as follows:

nested-navigators-RN

Create a file, src/navigation/Navigation.js, where we will manage navigation for the app.

src-structure

Building Stacks

Say I want to add tab icons for Home and Details in the Tab Navigator. From the Details tab I should be able to navigate to an inner screen named InnerDetails.js. So we will need to create separate stacks using the Stack Navigator for the two tabs that we want.

We want only one screen for the Home tab, which is the loaded by default when the app starts. The HomeStack would define that screen as follows:

1import { createStackNavigator } from "@react-navigation/stack";
2import Home from "@screens/Home";
3
4const HomeStack = createStackNavigator();
5const screenNames = {
6  home: "Home",
7};
8
9function HomeStackScreen() {
10  return (
11    <HomeStack.Navigator
12      headerMode="none"
13      screenOptions={{
14        gestureEnabled: true,
15        gestureDirection: "horizontal",
16      }}
17      initialRouteName={screenNames.home}
18    >
19      <HomeStack.Screen name={screenNames.home} component={Home} />
20    </HomeStack.Navigator>
21  );
22}

Next we want a Details tab button for navigating to the Details screen. Inside that screen we want a button that will display an inner screen named InnerDetails.js.

We will hide our Tab bar in this particular screen with getFocusedRouteNameFromRoute from @react-navigation/stack. With that in mind, the Details Stack would be as follows:

1import React, { useEffect } from "react";
2import { createStackNavigator } from "@react-navigation/stack";
3import { getFocusedRouteNameFromRoute } from "@react-navigation/native";
4import Details from "@screens/Details";
5import InnerDetails from "@screens/InnerDetails";
6
7const screenNames = {
8  details: "Details",
9  innerDetails: "InnerDetails",
10};
11const DetailsStack = createStackNavigator();
12
13function DetailsStackScreen({ navigation, route }) {
14  const tabHiddenRoutes = ["InnerDetails"];
15  useEffect(() => {
16    if (tabHiddenRoutes.includes(getFocusedRouteNameFromRoute(route))) {
17      navigation.setOptions({ tabBarVisible: false });
18    } else {
19      navigation.setOptions({ tabBarVisible: true });
20    }
21  }, [navigation, route]);
22  return (
23    <DetailsStack.Navigator
24      headerMode="none"
25      screenOptions={{
26        gestureEnabled: true,
27        gestureDirection: "horizontal",
28      }}
29      initialRouteName={screenNames.details}
30    >
31      <DetailsStack.Screen name={screenNames.details} component={Details} />
32      <DetailsStack.Screen
33        name={screenNames.innerDetails}
34        component={InnerDetails}
35      />
36    </DetailsStack.Navigator>
37  );
38}

Configuring the Tab Bar

Now we will add the two tabs to the Tab bar. We want the Home screen to be our landing page so we should specify the HomeStack as the initial route in the Tab Navigator.

It is also possible to add styles to the Tab bar for different orientations using the tabBarOptions property of the Tab Navigator. This can be useful for highlighting the active tab, for example, as we will demonstrate shortly.

To know more about the props to customize the Tab navigator have a look at https://reactnavigation.org/docs/bottom-tab-navigator/ .

The Tab bar with the Home and Details tabs would be as follows:

1import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
2
3const screenNames = {
4  homeStack: "HomeStackScreen",
5  detailsStack: "DetailsStackScreen",
6};
7const Tab = createBottomTabNavigator();
8
9function Navigation() {
10  return (
11    <Tab.Navigator
12      initialRouteName={screenNames.homeStack}
13      tabBarOptions={{
14        activeTintColor: "#688E26",
15        inactiveTintColor: "#6C6C6C",
16      }}
17    >
18      <Tab.Screen
19        name={screenNames.homeStack}
20        component={HomeStackScreen}
21        options={{
22          tabBarLabel: "",
23          tabBarIcon: ({ color, focused }) => (
24            <Icon
25              name={focused ? "ri-home-fill" : "ri-home-line"}
26              size="24"
27              color={color}
28            />
29          ),
30        }}
31      />
32      <Tab.Screen
33        name={screenNames.detailsStack}
34        component={DetailsStackScreen}
35        options={{
36          abBarLabel: "",
37          tabBarIcon: ({ color, focused }) => (
38            <Icon
39              name={focused ? "ri-information-fill" : "ri-information-line"}
40              size="24"
41              color={color}
42            />
43          ),
44        }}
45      />
46    </Tab.Navigator>
47  );
48}

Putting it all together

And here is the final Navigation.js code in the Navigation file (src/navigation/Navigation.js):

1import React, { useEffect } from "react";
2import { createStackNavigator } from "@react-navigation/stack";
3import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
4import Icon from "react-native-remix-icon";
5import Home from "@screens/Home";
6import Details from "@screens/Details";
7import InnerDetails from "@screens/InnerDetails";
8import { getFocusedRouteNameFromRoute } from "@react-navigation/native";
9
10export const screenNames = {
11  home: "Home",
12  details: "Details",
13  innerDetails: "InnerDetails",
14  homeStack: "HomeStackScreen",
15  detailsStack: "DetailsStackScreen",
16};
17
18const HomeStack = createStackNavigator();
19
20function HomeStackScreen() {
21  return (
22    <HomeStack.Navigator
23      headerMode="none"
24      screenOptions={{
25        gestureEnabled: true,
26        gestureDirection: "horizontal",
27      }}
28      initialRouteName={screenNames.home}
29    >
30      <HomeStack.Screen name={screenNames.home} component={Home} />
31    </HomeStack.Navigator>
32  );
33}
34
35const DetailsStack = createStackNavigator();
36
37function DetailsStackScreen({ navigation, route }) {
38  const tabHiddenRoutes = ["InnerDetails"];
39  useEffect(() => {
40    if (tabHiddenRoutes.includes(getFocusedRouteNameFromRoute(route))) {
41      navigation.setOptions({ tabBarVisible: false });
42    } else {
43      navigation.setOptions({ tabBarVisible: true });
44    }
45  }, [navigation, route]);
46
47  return (
48    <DetailsStack.Navigator
49      headerMode="none"
50      screenOptions={{
51        gestureEnabled: true,
52        gestureDirection: "horizontal",
53      }}
54      initialRouteName={screenNames.details}
55    >
56      <DetailsStack.Screen name={screenNames.details} component={Details} />
57      <DetailsStack.Screen
58        name={screenNames.innerDetails}
59        component={InnerDetails}
60      />
61    </DetailsStack.Navigator>
62  );
63}
64
65const Tab = createBottomTabNavigator();
66
67export default function Navigation() {
68  return (
69    <Tab.Navigator
70      initialRouteName={screenNames.homeStack}
71      tabBarOptions={{
72        activeTintColor: "#688E26",
73        inactiveTintColor: "#6C6C6C",
74      }}
75    >
76      <Tab.Screen
77        name={screenNames.homeStack}
78        component={HomeStackScreen}
79        options={{
80          tabBarLabel: "",
81          tabBarIcon: ({ color, focused }) => (
82            <Icon
83              name={focused ? "ri-home-fill" : "ri-home-line"}
84              size="24"
85              color={color}
86            />
87          ),
88        }}
89      />
90      <Tab.Screen
91        name={screenNames.detailsStack}
92        component={DetailsStackScreen}
93        options={{
94          abBarLabel: "",
95          tabBarIcon: ({ color, focused }) => (
96            <Icon
97              name={focused ? "ri-information-fill" : "ri-information-line"}
98              size="24"
99              color={color}
100            />
101          ),
102        }}
103      />
104    </Tab.Navigator>
105  );
106}

The following image shows the output: home-details

⌘K
    to navigateEnterto select Escto close
    Previous
    Next