Getting Started with React Navigation, the Navigation Solution for React Native

Published May 10, 2017Last updated Nov 06, 2017
Getting Started with React Navigation, the Navigation Solution for React Native

If you’ve worked with React Native for any amount of time you’ve likely been confused by how you navigate in your app (practices, packages, etc). I’ve used React Native since the very early days of it being opened sourced and have tried many solutions, this has been my navigator progression…

NavigatorIOS > Navigator > react-native-router-flux > NavigationExperimental or ex-navigation (depending on navigation requirements)

Each upgrade took time to refactor and introduce a new, slightly different, pattern for navigation and managing navigation state.

Fortunately, we’ve now got a single “official” solution to navigation in React Native with react-navigation. This solution will replace Navigator, NavigationExperimental, and ex-navigation becoming the new “official” navigation solution.

It’s currently in beta but is in a pretty stable state. If you’re starting a new React Native app I would say start with react-navigation, it will only get better from here.

Today, I want to take some time building an app with some common routing needs using react-navigation. We’ll cover tabs, stacks, and modals — this is just a very basic first look at the package. Here’s the end product

1.gif

Code

All the code for this tutorial can be found on Github. I’ll be starting with a simple app that has react-native-elements, a few screens made, and some user data from Random User Generator so that we can focus on the navigation rather than configuring the app.

Video

Prefer to learn from video? Check out a video version of this tutorial on Youtube.

Getting Started with React Navigation

Tabs

The first thing we’ll add is two tabs — one for a list of users, and one for details of the current user. To accomplish this we’ll use the TabNavigator from React Navigation.

We’ll be working in the config/router.js file. The first thing we need to do is import our pre-built screens — Feed.js and Me.js . We’ll also need to import TabNavigator from react-navigation and Icon from react-elements.

// router.js

import React from 'react';
import { TabNavigator } from 'react-navigation';
import { Icon } from 'react-native-elements';

import Feed from '../screens/Feed';
import Me from '../screens/Me';

With that completed we can actually start define our TabNavigator. The TabNavigator takes an object and each key/value pair is going to represent a tab.

// router.js

export const Tabs = TabNavigator({
  Feed: {
    screen: Feed,
  },
  Me: {
    screen: Me,
  },
});

You can see that we then pass a screen to each tab—this will be the component that should be rendered when that tab is active.

The last thing we need to do is use the Tabs navigator in our app entry point. The result of TabNavigator is simply a component and can be rendered like any other.

// index.js

import React, { Component } from 'react';
import { Tabs } from './config/router';

class App extends Component {
  render() {
    return <Tabs />;
  }
}

export default App;

2.png

However, we want to add an icon to the tab bar so things look right. There are two different ways to do it — one is to define that information on the screen itself via the navigationOptions static or, and this is my preferred way, to define it at the same point you define the tabs.

// router.js

export const Tabs = TabNavigator({
  Feed: {
    screen: Feed,
    navigationOptions: {
      tabBarLabel: 'Feed',
      tabBarIcon: ({ tintColor }) => <Icon name="list" size={35} color={tintColor} />
    },
  },
  Me: {
    screen: Me,
    navigationOptions: {
      tabBarLabel: 'Me',
      tabBarIcon: ({ tintColor }) => <Icon name="account-circle" size={35} color={tintColor} />
    },
  },
});

3.png

Tabs are very simple to set up when using TabNavigator and you’re able to quickly update and modify them.

Now when pressing a row item you likely, and correctly, expect that we go to a new screen as a result. For this we’ll use the StackNavigator where we add a new screen onto the stack.

The API is extremely similar to that of TabNavigator where it takes an object in which we define all the screens that should be available in that stack.

First, make sure you import StackNavigator from react-navigation. You’ll then also want to import UserDetail from '../screens/UserDetail';. We then define the StackNavigation, just like we did for the TabNavigator. We can then replace the Feed screen definition in the TabNavigator with our new stack.

I didn’t mention it before but in React Navigation, in addition to accepting components for the screen, it accepts other navigation stacks to display for a screen. That means that our FeedStack will be nested within our Tabs.

// router.js


// ...

export const FeedStack = StackNavigator({
  Feed: {
    screen: Feed,
  },
  Details: {
    screen: UserDetail,
  },
});

export const Tabs = TabNavigator({
  Feed: {
    screen: FeedStack, // Replaced Feed with FeedStack
    navigationOptions: {
      tabBarLabel: 'Feed',
      tabBarIcon: ({ tintColor }) => <Icon name="list" size={35} color={tintColor} />
    },
  },
  ...
});

4.png

Though this works it doesn’t look quite right, we want some sort of title for our navigation bar on the feed screen… Just like before we can leverage the navigationOptions to set it. Check out the docs on all that you’re able to do here.

// router.js

// ...

export const FeedStack = StackNavigator({
  Feed: {
    screen: Feed,
    navigationOptions: {
      title: 'Feed',
    },
  },
  Details: {
    screen: UserDetail,
    navigationOptions: ({ navigation }) => ({
      title: `${navigation.state.params.name.first.toUpperCase()} ${navigation.state.params.name.last.toUpperCase()}`,
    }),
  },
});

// ...

Now to actually navigate from our list item to the screen.

Whenever you define a screen that component will have access to navigation on this.props which you can use for a host of things. Here we’ll just be using it for the navigate function. In the Feed.js we’ll add the following to the onLearnMore function.

// Feed.js


// ..

class Feed extends Component {
  onLearnMore = (user) => {
    this.props.navigation.navigate('Details', { ...user });
  };

  ...
}

export default Feed;

Without completely rehashing the docs, we’re telling navigate which screen we want to go to (which aligns with the key in our StackNavigator) and the data we want to pass to the next screen. This leaves us with

5.gif

Now you may be wondering how we’re accessing the data being passed to the new screen. It, like the navigate function, is available on this.props.navigation — specifically this.props.navigation.state.params. I’m pulling all the data to generate the UserDetail.js screen like this

// UserDetail.js

const { picture, name, email, phone, login, dob, location } = this.props.navigation.state.params;

The final thing I want to cover in this tutorial is how to create a modal with React Navigation. It’s a common pattern but one that I’ve found surprisingly difficult/not very intuitive to implement in other react native routing solutions.

To accomplish a modal we’ll be creating another StackNavigator, with one subtle difference. This “Root” navigator will have our TabNavigator inside of it as well as our settings screen. We’ll also tell this StackNavigator to, rather than bring cards/screens in from right to left, to bring them from bottom to top. All those words looks like this in code (make sure to import the Settings screen if you haven’t already).

// router.js


// ...

export const Root = StackNavigator({
  Tabs: {
    screen: Tabs,
  },
  Settings: {
    screen: Settings,
  },
}, {
  mode: 'modal',
  headerMode: 'none',
});

You can see that this StackNavigator is exactly the same as the one we created before but we’re leveraging two options in it to make the modal interface we desired. In addition to the mode, which I described above, we have headerMode which tells the StackNavigator to not display a navigation bar for this stack — we’ll let the children navigators do that.

To keep a consistent interface and to show that you can nest a StackNavigator in a StackNavigator we’ll build one for the settings screen as well.

// router.js

// ...
export const SettingsStack = StackNavigator({
  Settings: {
    screen: Settings,
    navigationOptions: {
      title: 'Settings',
    },
  },
});

export const Root = StackNavigator({
  Tabs: {
    screen: Tabs,
  },
  Settings: {
    screen: SettingsStack,
  },
}, {
  mode: 'modal',
  headerMode: 'none',
});

Finally, we’ll want to tell the app entry point to use our new Root navigator instead of the Tabs.

// index.js


import React, { Component } from 'react';
import { Root } from './config/router';

class App extends Component {
  render() {
    return <Root />;
  }
}

export default App;

All of this leaves us with the following

6.gif

Remember, all of this code is available on Github and I encourage you to check it out as well as the react navigation docs.

If you’re interested in React Native and learning what goes into building production quality apps with it sign up for my email list. This post was originally published on Medium.

Discover and read more posts from Spencer Carli
get started
Enjoy this post?

Leave a like and comment for Spencer

7
7