Emmanuel Yusufu Aspiring Product Designer | Frontend and JS Developer | Certified digital marketer

React Native Navigation: Tutorial with examples

9 min read 2599

React Native Navigation: Tutorial With Examples

Editor’s note: This React Native Navigation tutorial was last updated on 1 December 2022 to include information on the latest version of React Navigation, v6 at the time of writing, as well as a section that compares React Navigation to the React Router library. Check out React Navigation vs. React Native Navigation: Which is right for you? for more information.

Mobile apps are made up of multiple screens. When building mobile apps, a primary concern is how to handle a user’s navigation through the app — e.g., the presentation of the screens and the transitions between them.

React Navigation is one of the most well-known navigation libraries available for React. In this tutorial, we’ll go through the basics of React Native navigation, show you how to get started using React Navigation in a React Native app, and walk through some React Native navigation examples.

We’ll cover the following:

What is React Navigation?

React Navigation is a standalone library that enables you to implement navigation functionality in a React Native application.

React Navigation is written in JavaScript and does not directly use the native navigation APIs on iOS and Android. Rather, it recreates some subset of those APIs. This allows for integration of third-party JS plugins, maximum customization, and easier debugging, with no need to learn Objective-C, Swift, Java, Kotlin, etc.

React Navigation 6.0

The most stable version of React Navigation at the time of writing is React Navigation 6.0, released in August 2021. According to their release post, the latest edition is focused on flexibility and ease of use. However, the team also stated that the latest release brings in very few breaking changes this time.

Some of the new features included in React Navigation 6.0 include:

  • Flexible customization options for navigators: This means that developers can now further tweak the look and feel of their navigators
  • Group component: The Group element lets users share screen options and configs between various screens in the project. As a result, this allows for more organization and code readability
  • Component library: As of v6, the Navigation team has created an elements component library. This is useful for cases where the user wants to build a custom navigator
  • Native navigation: By default, the library now uses native-stack under the hood. This means that navigation between heavy screens is faster and less demanding on older devices
  • Flipper plugin: This tool allows developers to debug their app and track performance. In the latest release, there is now an official React Navigation plugin to make the testing process easier

What is React Native Navigation?

React Native Navigation is a popular alternative to React Navigation. It’s a module that is dependent on and designed to be used with React Native. React Native Navigation differs slightly in that it directly uses native navigation APIs on iOS and Android, which allows for a more native look and feel.

For a more detailed exploration of the differences between React Navigation and React Native Navigation, check out “React Navigation vs. React Native Navigation: Which is right for you?

An alternative: React Router Native

React Router Native is another solution for implementing navigation functionality in a React Native app. It is developed by the Remix team, which is best known for its modern web framework.

React Router Native shares most of its API code with the React Router framework. This means that web developers who have worked with React Router will find it easy to jump into using its Native counterpart.

In terms of ease of use, both React Navigation and React Router Native are identical. For example, look at the following Router Native code:

import { NativeRouter, Route, Link } from "react-router-native";
const Home = () => <Text>Home</Text>;
const About = () => <Text>About</Text>;
const App = () => (
  <NativeRouter>
    <View>
      <View>
        {/* Define our links. They are like anchor tags */}
        <Link to="/">
          <Text>Home</Text>
        </Link>
        <Link to="/about">
          <Text>About</Text>
        </Link>
      </View>
      {/*Define our routes for this project*/}
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
    </View>
    {/*The NativeRouter*/}
  </NativeRouter>
);

When compared to Navigation, we can see that the code is similar:

import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
function HomeScreen() {
  return (
    <View>
      <Text>Home Screen</Text>
    </View>
  );
}
function AboutScreen() {
  return (
    <View>
      <Text>About Screen</Text>
    </View>
  );
}
const Stack = createNativeStackNavigator();
export default function App() {
  return (
    <NavigationContainer>
     <Stack.Navigator>
        {/*Define our routes*/}
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="About" component={AboutScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

The code used to implement routing in both libraries are identical to each other. This is a major plus point, since this means that there is a minor learning curve in both frameworks.

If you’re coming from a web development background, I would recommend React Router Native because its usage is the same as that of React Router. Otherwise, React Navigation should be the way to go because it has a larger community, which offers more development support.

Installing React Navigation

Assuming you have Yarn installed, the first step is to set up a React Native app. The easiest way to get started with React Native is with Expo tools because they allow you to start a project without installing and configuring Xcode or Android Studio.

First, initialize a blank Expo app using this bash command:

npx create-expo-app ReactNavigationDemo

This will kickstart the downloading process and configure the project:

Installing React Navigation

Next, cd into the project folder and open your code editor:

cd ReactNavigationDemo

If you are using VS Code, you can open the current folder in the editor using:

code .

Start the app with:

npx expo start

The next step is to install the react-navigation library in your React Native project:

npm install @react-navigation/native
npx expo install react-native-screens react-native-safe-area-context

The React Native stack navigator

React Navigation is built with JavaScript and lets you create components and navigation patterns that look and feel like truly native ones.

React Navigation uses what’s called a stack navigator to manage the navigation history and presentation of the appropriate screen based on the route taken by a user inside the app. Only one screen is presented to a user at a given time.

Imagine a stack of paper; navigating to a new screen places it on top of the stack, and navigating back removes it from the stack. The stack navigator also provides the transitions and gestures that feel like those of native iOS and Android. Note that an app can have more than one stack navigator.

React Native navigation examples

In this section, we’ll explore some examples of React Native navigation patterns and how to achieve them using the React Navigation library.

1. Using stack navigator to navigate between screen components

Let’s begin by first creating a /components folder in the root of our project. Then we create two files, Homescreen.js and Aboutscreen:

// Homescreen.js
import React from "react";
import { Button, View, Text } from "react-native";

export default function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to About"
        onPress={() => navigation.navigate("About")}
      />
    </View>
  );
}

Note the onPress prop of the button above — we’ll explain what it does later.

// Aboutscreen.js
import React, { Component } from "react";
import { Button, View, Text } from "react-native";
export default function AboutScreen() {
  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <Text>About Screen</Text>
    </View>
  );
}

Your project folder should look like this:

React Navigation Project Folder

Let’s also make some changes to App.js. Here, we’ll have to make the following imports:

//tell React that we will implement a navigator
import { NavigationContainer } from "@react-navigation/native";
//create a stack navigator
import { createNativeStackNavigator } from "@react-navigation/native-stack";

It is useful to implement our navigation in the root App.js file because the component exported from App.js is the entry point (or root component) for a React Native app, and every other component is a descendant.



As you will see, we will encapsulate every other component inside the navigation functions:

// App.js
import * as React from "react";
import { View, Text } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import HomeScreen from "./components/HomeScreen";
import AboutScreen from "./components/AboutScreen";

const Stack = createNativeStackNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="About" component={AboutScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

In the code above, createNativeStackNavigator provides a way for our app to transition between screens, where each new screen is placed on top of a stack. It is configured to have the familiar iOS and Android look and feel: new screens slide in from the right on iOS and fade in from the bottom on Android.

Here, we’ve executed the createNativeStackNavigator function and stored its instance into the Stack variable.

Later on, we will pass our routes using Stack.Screen tags. The Home route corresponds to the HomeScreen, and the About route corresponds to AboutScreen.

The createStackNavigator function passes behind the scenes, a navigate prop to the HomeScreen and AboutScreen components. This prop allows for navigation to a specified screen component. This is why we are able to use it on a button at HomeScreen.js, which, when pressed, leads to the AboutScreen page, as shown below:

<Button title="Go to About" onPress={() => navigation.navigate("About")} />;

In the App.js code, we finally created an app container by wrapping our components within NavigationContainer tags. This container manages navigation state.

To run the app, you’ll need to download the Expo client app. You can get the ‎iOS and Android versions. Make sure your command line is pointed to the project folder and run the following command:

npx expo start

You should see a QR code displayed on the terminal. Scan the QR code with the Expo app on Android, and for the iOS app, you can scan using the normal iPhone camera, which will prompt you with a command to click to open the Expo app:

Scanning QR Code To Open The Stack Nav Example

2. Using tab navigation

Most mobile apps have more than one screen. A common style of navigation in such mobile apps is tab-based navigation. Here we will focus on how to implement tab navigation using the createBottomTabNavigator function.

Before implementing tab-based navigation, we have to first install the bottom-tabs module like so:

npm install @react-navigation/bottom-tabs

Let’s add another screen in our app by creating a ContactScreen.js file under /components:

import React, { Component } from "react";
import { Button, View, Text } from "react-native";

export default function ContactScreen() {
  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <Text>Contact Screen</Text>
    </View>
  );
}

Now let’s add to the imports at the top of our App.js file:

import ContactScreen from './components/ContactScreen';

Recall that it is useful to implement our navigation in the root App.js component. Therefore, we will implement our tab navigation by importing createBottomTabNavigator in App.js. Let’s replace createStackNavigator with this line of code:

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

Also, run the createBottomTabNavigator function:

const Tab = createBottomTabNavigator();

//further code...
<NavigationContainer>
  <Tab.Navigator initialRouteName="Home">
    <Tab.Screen name="Home" component={HomeScreen} />
    <Tab.Screen name="About" component={AboutScreen} />
  </Tab.Navigator>
</NavigationContainer>;

Add the new screen using the Tab.Screen component:

<Tab.Navigator>
  <Tab.Screen name="Home" component={HomeScreen} />
  <Tab.Screen name="About" component={AboutScreen} />
  <Tab.Screen name="Contact" component={ContactScreen} />
</Tab.Navigator>

If you run the app with npm start and open it on your Expo client, you should see the bottom nav implemented:

Scanning QR Code To Open Bottom Nav Example

3. Using drawer navigation

To immediately begin implementing drawer navigation, first install the needed dependencies:

npm install @react-navigation/drawer
npx expo install react-native-gesture-handler react-native-reanimated

Next, head on to the Reanimated documentation to set up gesture control in your project.

After this step, write these import statements in App.js:

import "react-native-gesture-handler"; //this should be the first import in your code
import { createDrawerNavigator } from "@react-navigation/drawer";

Let’s also update the AppNavigator variable:

const Drawer = createDrawerNavigator();
<Drawer.Navigator initialRouteName="Home">
  <Stack.Screen name="Home" component={HomeScreen} />
  <Stack.Screen name="About" component={AboutScreen} />
  <Stack.Screen name="Contact" component={ContactScreen} />
</Drawer.Navigator>

If you npm start, you should be able to see the changes right away. Swipe from the left to see the drawer navigation:

Scanning QR Code To Open Drawer Nav Example

You can customize your drawer navigation by adding icons beside the route names. In the assets folder of this project, there are currently three icons:

Icons In The Assets Folder

We can customize by adding navigationOptions to the following screen component files:

<NavigationContainer>
  <Drawer.Navigator initialRouteName="Home">
    <Drawer.Screen
      name="Home"
      component={HomeScreen}
      options={{ //change the configuration of our screen
        drawerIcon: ({ color, number, focused }) => { //set the icon:
          return ( //the icon will be an image
            <Image
              source={require("../assets/home-icon.png")}
              style={{ height: 30, width: 30 }}
            />
          );
        },
      }}
    />
    <Drawer.Screen
      name="About"
      component={AboutScreen}
      options={{
        drawerIcon: ({ color, number, focused }) => { //set the icon for all screens
          return (
            <Image
              source={require("../assets/about-icon.png")}
              style={{ height: 30, width: 30 }}
            />
          );
        },
      }}
    />
    <Drawer.Screen
      name="Contact"
      component={ContactScreen}
      options={{
        drawerIcon: ({ color, number, focused }) => {
          return (
            <Image
              source={require("../assets/contact-icon.png")}
              style={{ height: 30, width: 30 }}
            />
          );
        },
      }}
    />
  </Drawer.Navigator>
</NavigationContainer>

Navigation Icons In The Drawer Nav

The drawerActiveTintColor prop lets you apply any color based on active or inactive states of navigation tabs and labels. For example, we can change the active state color for our nav drawer labels. Go to the Drawer.Navigator variable and add to the options object:

<Drawer.Navigator
  initialRouteName="Home"
  screenOptions={{ drawerActiveTintColor: "#e91e63" }}
>
//... further code.

This results in a change of color:

Color Change In The Drawer Nav Example

Passing parameters to screens in React Navigation

There are two simple steps to pass params to routes:

  1. Pass params to a route by putting them in an object as a second parameter to the navigation.navigate function:
    navigation.navigate('RouteName', { /* params go here */ })
  2. Read the params in your screen component:
    export default function HomeScreen({ route, navigation }) {
    
    //the 'route' variable gives us information on the page.
    
    //It also stores the parameters and their values
    
    const { paramName } = route; //our parameter 'paramName' is stored here.
    
    //..further code..
    
    }

Finally, to set the header title, we can use the title property of the options prop like so:

<Drawer.Screen
  name="Home"
  component={HomeScreen}
  options={{
    title: "Home Page", //set the title of the page to 'Home page'
  }}
/>

Setting Home Page Title

Conclusion

I hope that this article will jumpstart your use of the React Navigation package in your existing or future React Native projects.

There’s a lot more that can be done, and React Navigation will meet most of your needs. To learn more, check out the React Navigation documentation and feel free to grab the final code from my GitHub repo.

LogRocket: Instantly recreate issues in your React Native apps.

LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.

LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.

Start proactively monitoring your React Native apps — .

Emmanuel Yusufu Aspiring Product Designer | Frontend and JS Developer | Certified digital marketer

28 Replies to “React Native Navigation: Tutorial with examples”

  1. i have tried many time Using stack navigator to navigate between screen components but this is not working.

    1. Hello Rama, I’m sorry about that. Can you share a snippet of your code at emmayusufu [at] gmail.com ? It works for me.

  2. Hello m y friend, thanks for you explain, but i can run my project with expo and react navigation. But i want to run react navigation without expo and can’t, i have followed the indicates of the main page react native https://reactnavigation.org/docs/en/getting-started.html but dont work for me. Do you something about this ? is compatible react native with react navigation without expo ?
    Thank in advance.

    Regards
    Juan.

  3. Hi Juan, react-native apps can be routed with react navigation without expo. Maybe a more clearer description of the error you are facing will really aid in fixing the error

  4. Thanks for you answer, i think i didnt explain you well,
    I Proceed to explain you. how you know, there are two forms of work with react navigation, one it normal form(react-native init [Name o aplication]) and the another form is using EXPO(expo init [name of project])
    My purpose is use the react navigation libary but happens that I can use react navigation with expo very well, but when i try use the react navigation with the normal form i have trouble. I followed the steps of the official page (https://reactnavigation.org/docs/en/getting-started.html) without succes.
    The error that i have are:

    PS E:\AplicacionesReact\easyReactNavigation> react-native run-android
    error React Native CLI uses autolinking for native dependencies, but the following modules are linked manually:
    – react-native-gesture-handler (to unlink run: “react-native unlink react-native-gesture-handler”)
    This is likely happening when upgrading React Native from below 0.60 to 0.60 or above. Going forward, you can unlink this dependency via “react-native unlink ” and it will be included in your app automatically. If a library isn’t compatible with autolinking, disregard this message and notify the library maintainers.
    Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md
    info Running jetifier to migrate libraries to AndroidX. You can disable it using “–no-jetifier” flag.
    Jetifier found 913 file(s) to forward-jetify. Using 8 workers…
    info Starting JS server…
    info Installing the app…
    Starting a Gradle Daemon, 3 stopped Daemons could not be reused, use –status for details
    > Task :app:transformNativeLibsWithMergeJniLibsForDebug FAILED

    Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0.
    Use ‘–warning-mode all’ to show the individual deprecation warnings.
    See https://docs.gradle.org/5.5/userguide/command_line_interface.html#sec:command_line_warnings
    42 actionable tasks: 2 executed, 40 up-to-date

    FAILURE: Build failed with an exception.

    * What went wrong:
    Execution failed for task ‘:app:transformNativeLibsWithMergeJniLibsForDebug’.
    > Could not read path ‘E:\AplicacionesReact\easyReactNavigation\android\app\build\intermediates\transforms\mergeJniLibs\debug\0\lib\armeabi-v7a’.

    * Try:
    Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output. Run with –scan to get full insights.

    * Get more help at https://help.gradle.org

    BUILD FAILED in 26s

    error Failed to install the app. Make sure you have the Android development environment set up: https://facebook.github.io/react-native/docs/getting-started.html#android-development-environment. Run CLI with –verbose flag for more details.
    Error: Command failed: gradlew.bat app:installDebug -PreactNativeDevServerPort=8081

    FAILURE: Build failed with an exception.

    * What went wrong:
    Execution failed for task ‘:app:transformNativeLibsWithMergeJniLibsForDebug’.
    > Could not read path ‘E:\AplicacionesReact\easyReactNavigation\android\app\build\intermediates\transforms\mergeJniLibs\debug\0\lib\armeabi-v7a’.

    * Try:
    Run with –stacktrace option to get the stack trace. Run with –info or –debug option to get more log output. Run with –scan to get full insights.

    * Get more help at https://help.gradle.org

    BUILD FAILED in 26s

    at checkExecSyncError (child_process.js:623:11)
    at execFileSync (child_process.js:641:15)
    at runOnAllDevices (E:\AplicacionesReact\easyReactNavigation\node_modules\@react-native-community\cli-platform-android\build\commands\runAndroid\runOnAllDevices.js:94:39)
    at buildAndRun (E:\AplicacionesReact\easyReactNavigation\node_modules\@react-native-community\cli-platform-android\build\commands\runAndroid\index.js:158:41)
    at E:\AplicacionesReact\easyReactNavigation\node_modules\@react-native-community\cli-platform-android\build\commands\runAndroid\index.js:125:12
    at processTicksAndRejections (internal/process/task_queues.js:85:5)
    at async Command.handleAction (E:\AplicacionesReact\easyReactNavigation\node_modules\react-native\node_modules\@react-native-community\cli\build\index.js:164:9)
    PS E:\AplicacionesReact\easyReactNavigation>

    Please help me.
    Advance in thanks.

    Juan.

  5. hi, i need help with integrating screens. i tried so many things and my header is missing. i really hope you can help. thanks.

  6. Hello,
    Emmanuel Yusufu

    Very Good Article,

    Actually I am using React Native 0.58 and when I’m trying to switch between 2 pages but i am unable to see the switching button on the next page. So can you help me with this ??

  7. So simple. And clear. Thanks for sharing. The best way to explain with simple example.

  8. I am a flutter developer, but now I want to learn React also, after reading this post, I clearly understand about Navigation concept. Thanks!

Leave a Reply