Combining Drawer, Tab and Stack navigators in React Navigation 6 (part 2)

header image

This is part 2 of a 2-part react navigation tutorial Combining Drawer, Tab and Stack navigators in React Navigation 6. If you haven’t read that yet, please read it first here

Implementing navigation such that the Drawer and Tab navigators are visible in every screen isn’t a simple task. Simply put, the react navigation library is not designed in a way that this functionality is ready out-of-the-box.

nav final

When working with nested navigators, the navigation UI of the child navigator is present only in the screens which it contains. Because of this, in order to have BottomTabNavigator in every screen, it must contain every screen.

Since TabNavigator will contain all of our stacks, the only screen present in DrawerNavigator now becomes TabNavigator. But we still want to render ‘Home’, ‘My Rewards’ and ‘Location’ routes in the drawer. We’ll refactor CustomDrawerContent to render a custom list of items. To get the focused route, we’ll use a reference to the navigation object defined in App.js. Let’s begin!

Route items

For each screen we’ll have a configuration object that we’ll store in an array. Remember that TabNavigator is a screen as well, contained within DrawerNavigator as a Drawer.Screen:

Regardless of the navigation style I always use screens and routes to have a centralized place to make changes. Let’s jump to BottomTabNavigator:

BottomTabNavigator.js

We’ve added ‘MyRewardsStack’ and ‘LocationsStack’ as tab screens. Only routes with showInTab: true will render a tab. If you comment-out the if (!item.showInTab) section, you’ll get all of the tabs rendered:

all tabs

With the full code, the page looks the same as before:

filtered tabs

Note also that now the screen names aren’t hardcoded, we’re using the screens object to supply the names.

Let’s jump to DrawerNavigator:

DrawerNavigator.js

Now we’ve removed ‘MyRewardsStack’ and ‘LocationsStack’, and are rendering selected routes (in the previous code we rendered all of the Drawer.Screens, which in this case would be only HomeTabs screen). We have an issue right now - the focused check will not work since props.state.index will always return 0, we’re always in BottomTabNavigator screen:

no focused route

As a workaround, we need to find out the current route, and we’ll do that using a reference to the navigation object.

App.js

We’re sending this reference as a prop to DrawerNavigator where we can use it to check the focused route:

DrawerNavigator.js

In the first render the getCurrentRoute() will return undefined, in that case we know that the focused route is HomeStack. We then, for each Drawer route, check if its name matches the focusedRouteItem.focusedRoute. For example, if we are on the MyRewards screen (or any other screen we would define in that stack), its focusedRoute would be MyRewardsStack. We get the desired result:

nav final

Conclusion

Using react navigation, we have implemented Drawer, Tab and Stack navigation such that the drawer and bottom tab UI is visible in every app route. We’ve added custom styles and components for Tabs, Headers and Drawer Items. We have also centralised our configuration for each route.

What’s next?

For further customisation, you can start by exploring the screenOptions and options props. Perhaps add a HeaderRight component to Drawer’s screenOptions, or add a tabBarBadge to Tab Navigators screen options.

When adding a new screen to any stack (or adding a new stack) make sure to add that screen’s config to routes to make sure our navigators access all of the required information. Happy coding!

The complete project can be found on github

0 comments