Get Started with React Navigation 5 in React Native
Introduction to React Navigation 5 and the differences from previous versions

React Navigation — the standard routing solution for native apps
This article aims to help the reader get started with React Navigation that, covering the differences from previous versions of the package and some of the implications on your codebase. We’ll be discussing concepts and integration based on iOS, but the same concepts can also be applied to Android based projects too.
React Navigation 5 has now had its public release, that has come with a large amount of changes to the package, including component based rather than declarative navigator configuration, the compartmentalisation of the framework into various package using the @react-navigation/ group prefix, an expansion of the navigation prop with the introduction of the route prop, new hooks, and more.
These range of changes have made it easier to implement navigators within components, and at the same time giving the API more expandability, especially when it comes to theming and conditionally embedding navigators — such as controlling which navigators are rendered depending on whether the user is signed in or out of the app.
A big effort has also been made to align the package with the latest React advancements, that include custom hooks and supporting projects that only adopt functional components. Hooks are preferred over HOCs, becoming extremely evident given the React Navigation organisation have now moved HOCs into a “Compatibility package” called @react-navigation/compat. HOCs are therefore obsolete if you opt to use hooks, that offer a more streamlined API, but the compat tools are there to support projects that wish to upgrade one API at a time.
The React Navigation organisation have posted a document attempting to outline the changes from v4 to v5. It is worth reading for those familiar with React Navigation. If you are new to the package, check it out after this article.
React Navigation background
React Navigation has become the go-to routing solution for both iOS and Android projects in React Native; it is the standard library used to configure the navigation system within native apps, allowing the developer to easily configure their screen flow with a range of “navigator” types, that range from stack navigators (incrementally navigating deeper into a screen hierarchy), tab bar navigators (top or bottom tab bars that switch screens), and drawers (a modal that slides in and out from one side of the screen). React Navigation is a routing framework and is fully customisable by design, meaning developers can develop their own screen flows, too.
The framework itself, that now consists of a collection of packages, is well maintained. One can see this by visiting the frequent releases on GitHub where the main packages are regularly updated and documented. A good indication of adoption rate is the weekly downloads of the @react-navigation/native package, a dependency required by every project using the framework. It has been on an upwards trajectory since its public release at the start of the year (2020), showing positive signs of adoption given the drastic changes in the API.
With the strong response to the latest release, React Navigation is now probably in the strongest position in its history. A bit of time has also passed since those major API changes have been announced to, so we can safely assume they are here to stay and the developer community support them. If you were on the fence about upgrading your projects to the latest version, now would be a good time to make the change.
Moving the article forward, we’ll now install React Navigation 5 within a React Native project and discuss some of the changes, before moving onto embedding the various navigators, as well as the range of hooks now available.
Installing React Navigation
The official documentation have a detailed getting started document that details the packages needed to get React Navigation up and running. Simply install the core package, @react-navigation/native, with yarn:
# core package
yarn add @react-navigation/nativeReact Navigation also relies on other dependencies, that they instruct you to install straight after the above package. The versions of these dependencies differ whether you are in an Expo managed environment or a bare workflow, where installing these with expo will ensure the correct versions are installed:
# installing dependencies for expo projects
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view# or# installing dependencies for bare workflow
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-viewIf your version of React Native is < 0.60.0, you will need to link these packages. This article assumes that automatic linking is being used. Also note that pods (Cocoapoads) must be installed too, to manage native iOS dependencies. Running npx pod-install ios install this if you haven’t already.
React Navigation also relies on react-native-gesture-handler, a package that provides APIs to interact with a device via its native touch and gesture system, thus making UX interactions smoother and less processor intensive (E.g. cutting out middleware between the JavaScript layer and Objective-C layer, in additional to utilising the multi-threaded nature of Native APIs as opposed to React Native’s thread limitations). react-native-gesture-handler is simple to install, only being required in your app’s entry file (most likely App.jsx for React Native apps, or App.tsx in a Typescript environment).
// App.tsximport 'react-native-gesture-handler'
import React from 'react'
...This covers the core package installation. We can now explore how to set up some navigators within a project, as well as the packages associated with them.
Navigators Overview
As we’ve already ascertained, React Navigation 5 have compartmentalised their various navigators and utilities into separate dependencies. This offers a more streamlined way to manage components, and suggests that each navigator has a radically different implementation to justify this separation. With React Navigation being split into various packages, your projects will also contain slightly less source code, a small but notable improvement.
If you think of each screen in a navigator as a component, you understand how navigators work. A navigator simply injects components (JSX to render for each screen) into their containers, and provide APIs to navigate between them in the process.
Navigator Setup
Navigators need to be wrapped in the NavigationContainer object supplied by @react-navigation/native, that acts as a context that provides navigation state. Import and wrap your navigators with it like so:
import { NavigationContainer} from '@react-navigation/native'export const App = () => {
return (
<NavigationContainer>
{/* navigators go here */}
</NavigationContainer>
)
};NavigationContainer sits at the top level of your component hierarchy, although other contexts, your Redux store, or other utilities like cookie or custom theme management should wrap NavigationContainer.
A Note on Theming
Although theming is not a focus of this article, it is important to highlight here the major differences of themes from version 4:
- Themes can be supplied via the theme prop of
NavigationContainer, where you can either importDefaultTheme(supplied by React Navigation), or provide a custom theme object. This differs from the previous version where you just supplied either adarkorlightvalue to the theme, a somewhat unconventional way of component theming. - As navigators are now defined as components and not declared as a JSON configuration, themes can now be imported on a per-navigator basis, and do not actually have to be imported via the theme from of
NavigationContainer.
The following example takes the DefaultTheme object of React Navigation, and amends a couple of its properties before embedding it in NavigationContainer’s theme prop. This demonstrates how to use the supplied theming (that I personally think they nailed as a vanilla light and dark mode), and then apply your own tweaks to match your own colour scheme:
...
import { NavigationContainer, DefaultTheme } from '@react-navigation/native'export const App = () => { // some custom theme context
const { theme } = useTheme(); // overwrite `DefaultTheme` properties based on my theme
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: theme.bodyBg,
primary: theme.priCol,
},
};
//apply `MyTheme` to `NavigationContainer` theme prop
return (
<NavigationContainer theme={MyTheme}>
...
</NavigationContainer>
);
}Strangely, the theme prop is not mentioned in the official App Container docs, but is indeed demonstrated on the dedicated Theming page. Nonetheless, check out the prior to see the remaining props of NavigationContainer — if in fact more haven’t been left out of the official documentation.
For those interested, it appears from the TypeScript declarations that theme was not originally planned to be added to NavigationContainer, and was slapped on as an additional type:

If you are using VSCode as your IDE, CMD + Click NavigationContainer to open its implementation. Simply hovering over the component in TypeScript environments will display its signature and types.
Navigator Components
There are 3 main navigators that React Navigation comes bundled with, that are suitable for both iOS and Android based projects. These navigators and their respective packages are as follows.
Stack Navigator
@react-navigation/stack (official doc): The most vanilla navigator you’ll find, a Stack navigator will navigate from screen to screen in a hierarchical fashion:

To set up a stack navigator, declare a stack navigator object via the createStackNavigator method. From here, the Navigator and Screen components derived from this method can be used to embed and wrap your desired screens:
import { createStackNavigator } from '@react-navigation/stack'const DashboardStack = createStackNavigator();function Dashboard() {
return (
<DashboardStack.Navigator mode='modal' headerMode='none'>
<DashboardStack.Screen name="Home" component={HomeScreen} />
<DashboardStack.Screen name="Stats" component={StatsScreen} />
</DashboardStack.Navigator>
);
}This pattern of declaring a new navigator and taking the resulting Navigator and Screen components is consistent throughout all the supplied navigators.
As you can see, the component-based declaration differs a lot from the previous version, but is quite self explanatory. Notice the mode and headerMode props of DashboardStack.Navigator — these may be familiar to you if you have used React Navigation 4.
Essentially, the navigationOptions properties are now represented as props, all of which have been documented here.
In addition to this, each DashboardStack.screen also support an options prop to customise navigation options on a per-screen basis. Instead of defining headerMode='none' fort the entire navigator, we could disable the header to individual screens using options:
<DashboardStack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false
}}
/>There are quite a few ways to manipulate the header through options — check out all the options properties here.
Bottom Tabs Navigator
@react-navigation/bottom-tabs (official doc): Another valuable navigator that will set up the boilerplate for a tab bar navigator, popular amongst dashboard designs. This tab bar navigator offers quite a comprehensive solution, allowing the developer to customise the look and feel of tabs with ease.

The following gist demonstrates how to construct a Bottom Tabs navigator, with some customisation with SVGs and styling:

