So you got react-native running on the simulator…Great! but how are you going to allow users to navigate through your app? React-Native does not come with a navigation option out of the box. There are several options for libraries for but the top navigation libraries are currently react-native-navigation and react-navigation. This article will attempt to compare the current state of these 2 libraries to provide some information about which one fits the needs of your project. Example repos can be found here: https://github.com/Staceadam/blog-examples/tree/master/react-native/navigation
We are going to be comparing the following:
Versions:
React-Native-Navigation v7.13.0 React-Navigation v5.9.4
We are also going to be using the current version of react-native v0.64.0 which includes AndroidX support, default CocoaPods integration and package auto-linking.
React-Native-Navigation
Implements a single repo which can be included with npm install react-native-navigation
. If you are coming from a newly created project with react-native init
you can run npx rnn-script
which will automatically modify the native code that is required. If not its a bit of a more manual process.
iOS https://wix.github.io/react-native-navigation/docs/installing#ios
Android https://wix.github.io/react-native-navigation/docs/installing#android
React-Navigation
Uses a more modular approach, the base scoped package is installed @react-navigation/native
but in order for the navigation to work it relies on several other external packages react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
iOS Auto links with pod install
Android Uses auto-linking
Layouts Both libraries require an initial setup/layout in order to convert your react components into their navigation system.
React-Native-Navigation
Requires you to edit the main index.js
by importing the Navigation library, registering each component you want to navigate with, and then using registerAppLaunchedListener method that will fire after the app has successfully launched. Inside of this method you make use of the setRoot method which you provide a configuration object that sets the base structure of your app. A basic implementation would look like...
import { Navigation } from 'react-native-navigation'
import App from './App'
Navigation.registerComponent('com.myApp.WelcomeScreen', () => App)
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot({
root: {
stack: {
children: [
{
component: {
name: 'com.myApp.WelcomeScreen'
}
}
]
}
}
})
})
These configuration objects allow you to customize your navigation experience in any direction but end up being deeply nested and hard to reason about when making a more complex app.
const bottomTabs = {
children: [
{
stack: {
children: [],
options: {
bottomTab: {
text: 'Tab 1',
icon: require('../images/one.png')
}
}
}
},
{
component: {
name: 'secondTabScreen',
options: {
bottomTab: {
text: 'Tab 2',
icon: require('../images/two.png')
}
}
}
}
],
options: {}
}
React-Navigation
Implements a more modular approach. The entry point is the index.js and still uses AppRegistry.registerComponent(appName, () => App)
but react-navigation provides you with a NavigationContainer that you wrap your app in, allowing you to compose any Navigator Components that the package exposes.
import 'react-native-gesture-handler'
import * as React from 'react'
import { NavigationContainer } from '@react-navigation/native'
export default function App() {
return (
<NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
)
}
Based on your applications navigation needs you can then utilize the following functions, all of which are exported from a base @react-navigation
scope. e.g. @react-navigation/stack
or @react-navigation/drawer
.
A simple implementation of a stack navigator might look like this...
import * as React from 'react'
import { createStackNavigator } from '@react-navigation/stack'
import { Home, Notifications, Profile, Settings } from '../screens'
const Stack = createStackNavigator()
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Notifications" component={Notifications} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
)
}
export default MyStack
This not only feels more like a react api but also allows for a lot more customization in a flat and readable secondary options object.
React-Native-Navigation
In order to navigate within react-native-navigation you import Navigation from the module which gives you access to all of the navigation methods such as push, pop, showModal
etc. Most of which will take in a props.componentId
that was passed in when the screen was registered and a layout object describing how the navigation should resolve.
Navigation.push(props.componentId, {
component: {
name: 'example.PushedScreen',
passProps: { text: 'Pushed screen' },
options: { topBar: { title: { text: 'Pushed screen title' } } }
}
})
React-Navigation
Handles navigating by passing a navigation
prop down to any registered screen within the app. This prop has access to navigate, goBack
functions at a base as well as navigator dependent functions like push, pop
when using a select navigators.
props.navigation.navigate('Profile');
The metric used to determine a react-native applications performance is frames per second(fps). Maintaining 60fps during all app states would be result in the optimal user experience and would make the UI appear fully responsive.
React-Native-Navigation Sets up your application with each screen being its own react application. This ends up being more performant because the components that need to render are already set up.
React-Navigation Works on the same JS thread that all of your application runs on. When you push a new route that JS thread needs to first render the component in order to send the proper commands over to the native side to create the backing views. This can cause a bottleneck in performance on apps with a large amount of screens and more complex user interfaces.
As of version 2.14.0 react-navigation has used an additional library called react-native-screens which utilize UIViewController
for iOS, and FragmentActivity
for Android. These help optimize memory usage and greatly simplifies the native view hierarchy.
React-Native-Navigation Maintainer: WIX Stars: 11.1k Used by: 7.2k Activity: Very active. Has minor releases coming out every 3–5 days. Focus: Bug fixes and api improvements.
Wix is using this library in their own react-native application and a lot of updates are born through it’s progression.
React-Navigation Maintainer: Expo and React-Native core members Stars: 17.2k Used by: 144k Activity: Very active. Has minor releases coming out every 3–5 days. Focus: Performance, bug fixes and api improve
Being maintained by core members of React-Native and Expo means React-Navigation is closer to what is happening within React-Native itself.
Choosing the right navigation library for you react-native application is a huge part in making sure it’s going to be able to be maintainable and scalable. It is the backbone of your application. Both of these libraries have their own unique solution for this issue and have a bright future. I highly suggest that you create separate apps using both libraries, go through the starter example and explore each.
React-Native-Navigation only shines on projects that have a lot of very complex screens where performance is a key concern. Further customization can require you to dive into Objective C / Swift / Java / Kotlin.
I would say that for the majority of applications React-Navigation is going to fit the bill. It’s easier to setup, has a more intuitive api and allows more customization with it being built on top of React primitives.