Complex Navigation Example with React Navigation
Tags: reactnative, react, mobiledevelopment, iosdevelopment
Published: 2019-06-05 15:53:37

---
title: 'Complex Navigation Example with React Navigation'
published: true
description: 'React Navigation is the go to solution for navigation in React Native apps.' 
tags: React Native, React, Mobile Development, iOS Development
canonical_url: https://www.reactnativeschool.com/complex-navigation-example-with-react-navigation
---

I've found people can be intimidated by combining different navigators in React Navigation to accomplish more "complex" navigation patterns. Today I want to briefly walk you through a more complex navigation set up. It will include:

- Switch Navigator used to represent our authenticated vs. un-authenticated app status
- Stack Navigator for normal right-to-left navigation in numerous places (authentication screens, stacks for each tab)
- Stack Navigator for bottom-to-top navigation
- Tab Navigator
- Drawer Navigator

> This post was originally published on [React Native School](https://www.reactnativeschool.com/). If you're interested in learning more about React Native School give us a visit!

## Prereq

Before we get started I'll be adding an `Exampe.js` file to serve as a screen for all of our routes (it is just a demo after all). This component generates a random background color and will display all of the available routes from the current screen.

<p class="code-header">Example.js</p>

```jsx
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';

const getAvailableRoutes = navigation => {
  let availableRoutes = [];
  if (!navigation) return availableRoutes;

  const parent = navigation.dangerouslyGetParent();
  if (parent) {
    if (parent.router && parent.router.childRouters) {
      // Grab all the routes the parent defines and add it the list
      availableRoutes = [
        ...availableRoutes,
        ...Object.keys(parent.router.childRouters),
      ];
    }

    // Recursively work up the tree until there are none left
    availableRoutes = [...availableRoutes, ...getAvailableRoutes(parent)];
  }

  // De-dupe the list and then remove the current route from the list
  return [...new Set(availableRoutes)].filter(
    route => route !== navigation.state.routeName
  );
};

const getRandomColor = () => {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
};

const Example = ({ navigation }) => {
  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: getRandomColor(),
      }}
    >
      {getAvailableRoutes(navigation).map(route => (
        <TouchableOpacity
          onPress={() => navigation.navigate(route)}
          key={route}
          style={{
            backgroundColor: '#fff',
            padding: 10,
            margin: 10,
          }}
        >
          <Text>{route}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

export default Example;
```

With that done lets get started.

## Switch Navigator

To accomplish the switching between different "states" of a user's journey we'll use a switch navigator so the user can't go back. Obviously, we'll have a screen for the main app journey. We'll also have one for un-authenticated users.

Additionally, I like to add a `Loading` screen of sorts. Typically this won't display anything - it's just there to determine if a user is authenticated or not and route them to the right place.

![](01.gif)

<p class="code-header">index.js</p>

```jsx
import React from 'react';
import {
  createAppContainer,
  createBottomTabNavigator,
  createDrawerNavigator,
  createStackNavigator,
  createSwitchNavigator,
} from 'react-navigation';

import Example from './screens/Example';

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: Example,
  },
  App: {
    screen: Example,
  },
});

export default createAppContainer(App);
```

## Auth Stack Navigator

If a user is not authenticated we'll set up a Stack navigator for them to go from a landing screen, sign in, create account, forgot password, or reset password. The typical options you see when you need to authenticate.

![](02.gif)

<p class="code-header">index.js</p>

```jsx
// ...

const AuthStack = createStackNavigator({
  Landing: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Landing',
    },
  },
  SignIn: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Sign In',
    },
  },
  CreateAccount: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Create Account',
    },
  },
  ForgotPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Forgot Password',
    },
  },
  ResetPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Reset Password',
    },
  },
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: Example,
  },
});

export default createAppContainer(App);
```

## App Tabs

Once the user is in the app we'll use tabs to give them the ability to access the main features of our app - a feed, search, and a discover page. We'll then replace the `App` item in our `App` navigator with the result of creating our tabs.

The output of creating any navigator is just a component so we can nest them infinitely in React Navigation.

![](03.gif)

<p class="code-header">index.js</p>

```jsx
// ...

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: MainTabs,
  },
});

// ...
```

## Stack Navigator for Each App Tab

Just like we nested the `MainTabs` in our `App` navigator we'll allow each tab in our app to have its own stack navigator. Doing it this way means that each tab is going to carry its own state so a user can go to the details screen of one tab, switch to another, and when coming back are able to maintain the same state for each tab.

Additionally, with this example you can see that navigators will grab the closest matching route name. That means we can reuse screen names and each stack will just grab the closest available `Details` screen either in its stack or above it in the navigator hierarchy.

![](04.gif)

<p class="code-header">index.js</p>

```jsx
const FeedStack = createStackNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Feed',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const SearchStack = createStackNavigator({
  Search: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Search',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const DiscoverStack = createStackNavigator({
  Discover: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Discover',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: FeedStack,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: SearchStack,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: DiscoverStack,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});
```

## App Drawer

Same story with a drawer. We'll create the navigator (we're also creating a settings stack to give us a reason for the drawer) and render that as a screen.

This time we'll replace rendering `MainTabs` with `MainDrawer` and render our tabs within the drawer. Building this hierarchy means we're just adding more navigators but everything that was already there will continue to work.

![](05.gif)

<p class="code-header">index.js</p>

```jsx
// ...

const SettingsStack = createStackNavigator({
  SettingsList: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Settings List',
    },
  },
  Profile: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Profile',
    },
  },
});

const MainDrawer = createDrawerNavigator({
  MainTabs: MainTabs,
  Settings: SettingsStack,
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: MainDrawer,
  },
});

// ...
```

## Modal Style Stack Navigator

Finally, we want to add a navigator that moves from bottom to top and will cover any other screen. That means it needs to be at the root most position of our stack. If it's at the root then it will be available to be rendered from any of its children.

![](06.gif)

<p class="code-header">index.js</p>

```jsx
// ...

const AppModalStack = createStackNavigator(
  {
    App: MainDrawer,
    Promotion1: {
      screen: Example,
    },
  },
  {
    mode: 'modal',
    headerMode: 'none',
  }
);

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: AppModalStack,
  },
});

export default createAppContainer(App);
```

## Final Navigator Code

Our final code.

<p class="code-header">index.js</p>

```jsx
import React from 'react';
import {
  createAppContainer,
  createBottomTabNavigator,
  createDrawerNavigator,
  createStackNavigator,
  createSwitchNavigator,
} from 'react-navigation';

import Example from './screens/Example';

const AuthStack = createStackNavigator({
  Landing: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Landing',
    },
  },
  SignIn: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Sign In',
    },
  },
  CreateAccount: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Create Account',
    },
  },
  ForgotPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Forgot Password',
    },
  },
  ResetPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Reset Password',
    },
  },
});

const FeedStack = createStackNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Feed',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const SearchStack = createStackNavigator({
  Search: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Search',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const DiscoverStack = createStackNavigator({
  Discover: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Discover',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: FeedStack,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: SearchStack,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: DiscoverStack,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});

const SettingsStack = createStackNavigator({
  SettingsList: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Settings List',
    },
  },
  Profile: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Profile',
    },
  },
});

const MainDrawer = createDrawerNavigator({
  MainTabs: MainTabs,
  Settings: SettingsStack,
});

const AppModalStack = createStackNavigator(
  {
    App: MainDrawer,
    Promotion1: {
      screen: Example,
    },
  },
  {
    mode: 'modal',
    headerMode: 'none',
  }
);

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: AppModalStack,
  },
});

export default createAppContainer(App);
```

You can find a [working example on Snack](https://snack.expo.io/@spencercarli/complex-react-navigation-example).