Exactly two years ago, we published the first stable version of React Navigation. Throughout this time, the library has been actively developed by adding many new features and bug fixes. The essence of React Navigation was that it was a project that was to become not only a project of individual programmers adapting it to their requirements, but a community as a whole, hence the emphasis on versatility, extensibility, and the tendency to reconsider the assumptions if there were such needs. Thanks to this, the Library has been undergoing metamorphosis of both incremental and completely reorganized shape.
This led us to the moment when React Navigation became one of the most popular navigation solutions in React Native and we are incredibly proud of it.
Today is the day, when we want to mark this vision and major refactoring of the project, that has taken place over the last six months, as stable. One could say that this significant change concerned the core library, and therefore the API, which has been developed and made more dynamic.
Highlights
Component based configuration
In previous versions of React Navigation, we used to configure the navigator statically using createXNavigator functions and static navigationOptions. In React Navigation 5, all of the configuration happens inside a component and is dynamic.
This means we have access to props, state and context, and can dynamically change the configuration for the navigator!
We want to stress that this is the most important change. This seems to be just a difference in the API. It actually required reconsidering many of the assumptions made in React Navigation during the development of previous versions. The static API, known from previous versions may seem an easier and more obvious choice. In the current version, the navigation configuration is consistent with all patterns in the React community.
This made it necessary to rewrite the core of the library, which allowed us to make a number of improvements not only in this respect.
New hooks
Hooks are great for stateful logic and code organization. Now we have several hooks for common use cases:
We’ve added a new setOptions method on the navigation prop to make configuring screen navigation options more intuitive than its static navigationOptions predecessor. It lets us easily set screen options based on props, state or context without messing with params. Instead of using static options, we can call it anytime to configure the screen.
It can be used for things like adding a button in the header which needs to interact with the screen state.
New theming API
In React Navigation, we had basic theming support where you could specify whether to use a light or dark theme. It wasn't easy to customize the colors used by the built-in components such as header, tab bar etc. without extra code or repetition.
Now, we have revamped the theme system for easier customization. It is possible to provide a theme object with your desired colors for background, accent color etc. and it will automatically change the colors of all navigators without any extra code.
The new version has been written from the ground-up with TypeScript. We now get first class autocompletion and type-checking.
We also have JSDoc for the built-in methods and options, so you get their description directly in your editor. See our typescript documentation for more details on how to use it.
Redux DevTools integration
If you use React Native Debugger or Redux Devtools Extension, you can see navigation actions in the devtools along with the current navigation state. It also supports time-travel debugging!
You don't need to use Redux in your apps for this to work and it works without any extra setup!
Native Stack Navigator
Traditionally, we have written our navigators in JavaScript for greater customizability. It fits a lot of use cases, but sometimes you want the exact native feel and the performance of native navigation. Now, we have added a new native stack navigator that uses native navigation primitives for navigation using the react-native-screens library. Under the hood, it just uses native components which might be obvious choice for native development and might be a good pick in the most cases.
This is a big release and, while the basic concepts such as nesting are the same, the new API is mostly incompatible with the previous API. We know it can be a challenge to upgrade your code base. So we're going to keep supporting React Navigation 4 with bug fixes. We'll accept contributions and keep it compatible with the latest React Native version. The old code will now live in the 4.x branch on GitHub.
We recommend starting your new projects with the new version so you can take advantage of the new APIs and the new features.
We have written an upgrade guide which will give you an overview of what's changed and how to adapt the old API and concepts to the new API.
A note for alpha users
If you were using React Navigation 5 when it was alpha, you might need to check the following changes when upgrading:
If you have added @react-navigation/core to your dependencies, remove it, and replace all imports from @react-navigation/core with @react-navigation/native
If you were importing NavigationNativeContainer, change it to NavigationContainer, if you were using NavigationContainer, change it to BaseNavigationContainer
If you had deep linking configured, the config format has changed for nesting. Check the deep linking docs for details.
Additionally, I would like to mention how important is the community's influence on building this project. Without you, your support, your readiness to test and apply the next versions of the libraries, it wouldn't be possible at all, and despite mentioning a few people by name, at no stage do we forget that this is a community activity and will respond to its needs. We are incredibly grateful for the opportunity to work on such an unusual project, which facilitates the work of a large number of users.
Thanks again and hope you will find this release useful.
This is a guest post by the React Native Paper team. If you like this guide, check out React Native Paper for more!
In this blog post, we'll show you how to build a Twitter clone app using React Navigation v5 and Paper.
Introduction
The React Navigation v5 comes with many great improvements compared to previous version. It not only provides a cross-platform native Stack, but also the API was redesigned from the ground up to allow things that were never possible before. Thanks to the component-based API, all of the configuration is happening inside the render method. This means we can access props, state and context and can dynamically change configuration for the navigator.
What is React Native Paper?
React Native Paper is a UI component library that implements MD Guidelines.
It allows building beautiful interfaces on Mobile and Web with high-quality cross-platform components.
Furthermore, Paper provides you with a full theming support, accessibility, RTL and it will take care of platform adaptation. This means you can focus on building apps with ready to use components instead of reimplementing the boring stuff.
In this guide, we would like to show you how to integrate React Navigation with Paper's components. To show all the details of the integration we've decided to build a clone of Twitter. Of course, the functionalities will be very limited but the navigation part and main screens should look and feel similar.
In the following gif, you can see what is the final version of the app gonna looks like:
Overview of the App
Since original Twitter is a very complex app, we will build only a part of it. This means we will implement:
Drawer
Stack Navigator with two screens: Screen showing bottom navigation and Details of a tweet
Bottom navigation with 3 tabs: Feed, Notifications, and Messages
I will focus this guide on a React Navigation and React Native Paper integration. It means I won't show you how to build all of the components necessary to create such an app, but you can always check the full implementation in the github repo.
Let's get started!
Getting started
I assume you already have an Expo project running locally. If not, make sure to create one. I chose Expo over plain React-Native because it includes most of the dependencies that we need so there is less work to do for us.
Let's install React Native Paper, React Navigation v5 and other required dependencies.
After you run these two commands you should be ready to go. Let's start implementing the app!
React Navigation and React Native Paper initial setup.
Both these libraries require minimal setup.
In case of React Native Paper, we need to wrap the component tree with a Provider. You can do this inside the exported component in the App.js file.
import React from'react';import{ Provider as PaperProvider }from'react-native-paper';import Main from'./src/Main';exportdefaultfunctionApp(){return(<PaperProvider><Main/></PaperProvider>);}
The PaperProvider provides the theme to all the components in the framework. It also acts as a portal to components that need to be rendered at the top level. Check the full Getting-Started page for more information.
React Navigation setup looks similar. There is a component called NavigationContainer which manages our navigation tree and contains the navigation state. It must wrap all navigator structure. We will render this component in App.tsx inside a PaperProvider. More information can be found in the official documentation.
import React from'react';import{ Provider as PaperProvider }from'react-native-paper';import{ NavigationContainer }from'@react-navigation/native';import Main from'./src/Main';exportdefaultfunctionApp(){return(<PaperProvider><NavigationContainer><Main/></NavigationContainer></PaperProvider>);}
Drawer
In our Twitter clone, we want to implement a Drawer that is available from any screen in the app. This means it has to be a topmost navigator.
In React Navigation v5 there is a common pattern for creating navigators. After importing createXNavigator function from the navigator package of your choice you can use Navigator and Screen components from the value it returns.
We can open a drawer with a swipe gesture, it looks very smooth. However, the UI doesn't look very impressive so let's add more content to the drawer to make it look just like in the final version.
We will use:
DrawerContentScrollView and DrawerItem from @react-navigation/drawer
Avatar, Text and Switch from react-native-paper
DrawerContentScrollView component makes the drawer vertically scrollable and provides support for devices with notches, so it's highly recommended to use it even for custom drawers.
Components from React Native Paper make a clean, material UI.
Stack Navigator provides a way for an app to transition between screens when each new screen is placed on top of a stack. In case of this Twitter clone, we will use it to transition from a screen displaying a feed of tweets to the screen showing details of a tweet.
React Navigation v5 provides two implementations of a Stack Navigator
Native Stack
JS-based Stack
The main difference between them is that JS-based stack re-implements animations and gestures while the native stack navigator relies on the platform primitives for animations and gestures.
In this section, we will integrate React Native Paper Appbar and JS-based Stack Navigator.
As a first step, we will create a minimal version of a Stack:
By default, the stack navigator is configured to have the familiar iOS and Android header. That doesn't suit our needs, because we want to use Paper's Appbar instead.
We can achieve that by passing an Appbar.Header component as a header in Stack's screenOptions. We will also pass a headerMode prop with a value of screen to have a nice looking fade in/out animation.
The Function that we pass to header prop has access to 3 properties:
scene
previous
navigation
Thanks to the scene property we can access the title of topmost screen on the stack and display it in the header.
Previous property tells us if there are any other screens lower on the Stack.
Finally, navigation property allows navigating to different screens e.g. opening a Drawer.
The thing that we haven't covered yet and it is very important is how to actually navigate between Stack Navigator screens.
In case of Tab or Drawer Navigator, we get it out of the box. We can swipe to open/close the Drawer or press a tab to change the scene. In Stack, we have to implement it by ourselves.
React Navigation gives us many different ways to navigate, but we will mostly focus on push and pop. You can access these two methods in navigation prop.
As the name suggests push method pushes the new screen on the stack and pop removes current screen from the stack.
As you can see on a snippet above, we invoke a navigation.pop function whenever user presses the back button in header. This means user will be allowed to come back from Details to the Feed screen.
We still need to implement an option to go from Feed to the Details. We can do it by invoking navigation.push('Details') whenever user presses a Tweet.
The implementation of Feed and Details components is quite big and complex, that's why I am not gonna post it here. Please make sure to check it out on github repo
We have covered only the basics of navigating between screens. If you want to learn more details check the official documentation.
Now, let's see what does the app looks like with Stack Navigator and Paper's Appbar.
We still miss the last piece of our navigation flow - Tab Navigator. Let's move to the next section where we will take care of it.
Bottom Navigation
In this section, we will implement a Tab Navigator with 3 tabs and we will make sure this component is now a one of Stack's screen.
We will use a Bottom Navigation component from React Native Paper that is exposed via @react-navigation/material-bottom-tabs package.
Let's import the createMaterialBottomTabNavigator function first.
Then we can get a reference to the Tab.Navigator and Tab.Screen components.
const Tab =createMaterialBottomTabNavigator();
Now, we are ready to build the actual Bottom Navigation. We will render a Tab.navigator and 3 Tab.Screen components as children. Each Tab.Screen representing a tab.
When we check the screen of the phone now, we will see a nice looking, material bottom navigation. What's more, Stack Navigator integrates nicely with Tab.Navigator and we can still navigate to the tweet Details screen.
FAB and Portal
As it is stated in Material Design Guidelines, the purpose of the FAB button is to give easy access to the main action of the application. Of course, the official Twitter app follows this pattern. Based on the type of screen, it allows creating new tweets or sending direct messages via FAB. It also smoothly animates the icon of the FAB when the user changes the tab and hides the FAB completely on specific screens.
In this section, we are going to implement the very same behaviour in our app. We are going to use a FAB and Portal components from React Native Paper.
Portal allows rendering a component at a different place in the parent tree. It means you can use it to render content that should appear above other elements, similar to Modal.
As an initial step, we will render a FAB on all tabs and then we will add additional functionalities.
Let's render a FAB and Portal in the same component where we render Tabs:
With just a few lines of JSX we have a nice looking FAB displayed on all tabs. Let's implement hiding it whenever the user goes to the tweet details screen.
Our current navigation structure should be:
StackNavigator that has two screens
The First screen of StackNavigator renders a TabNavigator with 3 tabs
The Second screen of StckNavigator renders a Tweet details
This means a component that renders TabNavigator is a Stack's screen. Thanks to that, we can use useIsFocused hook provided by @react-navigation/native and conditionally hide FAB.
import React from'react';import{ createMaterialBottomTabNavigator }from'@react-navigation/material-bottom-tabs';import{ useTheme, Portal,FAB}from'react-native-paper';import{ useIsFocused }from'@react-navigation/native';import{ Feed }from'./feed';import{ Message }from'./message';import{ Notifications }from'./notifications';const Tab =createMaterialBottomTabNavigator();exportconstBottomTabs=()=>{const isFocused =useIsFocused();return(<React.Fragment><Tab.NavigatorinitialRouteName="Feed"backBehavior="initialRoute"shifting={true}><Tab.Screenname="Feed"component={Feed}options={{
tabBarIcon:'home-account',}}/><Tab.Screenname="Notifications"component={Notifications}options={{
tabBarIcon:'bell-outline',}}/><Tab.Screenname="Messages"component={Message}options={{
tabBarIcon:'message-text-outline',}}/></Tab.Navigator><Portal>
<FAB
visible={isFocused} // show FAB only when this screen is focused
icon="feather"
style={{
position:'absolute',
bottom: safeArea.bottom +65,
right:16,}}
/>
</Portal></React.Fragment>);};
In the last step we will add ability to show different icon depending on the active tab.
We will take an advantage of our BottomTabs component being one of a Stack's screen. It means it has an access to the route object that is passed to each screen as a prop. This object contains an information about current screen which means we can access it and conditionally render proper icon. This is not a very common pattern and it can be confusing at first, so make sure to read the whole guide on how to use it and what can be achieved by using it.
import React from'react';import color from'color';import{ createMaterialBottomTabNavigator }from'@react-navigation/material-bottom-tabs';import{ Portal,FAB}from'react-native-paper';import{ useIsFocused }from'@react-navigation/native';import{ Feed }from'./feed';import{ Message }from'./message';import{ Notifications }from'./notifications';const Tab =createMaterialBottomTabNavigator();exportconstBottomTabs=props=>{// Get a name of current screenconst routeName = props.route.state
? props.route.state.routes[props.route.state.index].name
:'Feed';const isFocused =useIsFocused();let icon ='feather';switch(routeName){case'Messages':
icon ='email-plus-outline';break;default:
icon ='feather';break;}return(<React.Fragment><Tab.NavigatorinitialRouteName="Feed"shifting={true}><Tab.Screenname="Feed"component={Feed}options={{
tabBarIcon:'home-account',
tabBarColor,}}/><Tab.Screenname="Notifications"component={Notifications}options={{
tabBarIcon:'bell-outline',
tabBarColor,}}/><Tab.Screenname="Messages"component={Message}options={{
tabBarIcon:'message-text-outline',
tabBarColor,}}/></Tab.Navigator><Portal><FABvisible={isFocused}icon={icon}style={{
position:'absolute',
bottom:100,
right:16,}}color="white"/></Portal></React.Fragment>);};
As you can see on the gif, the FAB button works in the same way as in a Twitter app.
What's more, it even animates icon change properly even though we haven't implemented it. That's the behavior we get from React Native Paper's FAB out of the box.
Theming
Nowadays, supporting the Light/Dark theme is no longer a fancy way to stand out from other apps, but it has become a standard. Happily, both React Navigation v5 and React Native Paper supports theming and in this section I'll guide you through setting it up.
React Navigation
React Navigation exports two themes:
DefaultTheme
DarkTheme
We can import them from @react-navigation/native package and pass to NavigationContainer to apply the theme:
React Native Paper similarly to React Navigation also exports two themes:
DefaultTheme
DarkTheme
Once we import a theme we can pass it to the Paper's Provider component:
import*as React from'react';import{ NavigationContainer, DarkTheme }from'@react-navigation/native';import{
DarkTheme as PaperDarkTheme,
Provider as PaperProvider,}from'react-native-paper';exportdefaultfunctionMain(){return(<PaperProvidertheme={PaperDarkTheme}><NavigationContainertheme={DarkTheme}>{/* content */}</NavigationContainer></PaperProvider>);}
Combining themes
Since both React Navigation and React Native Paper follows the same pattern for theming and structure of the theme object is very similar, we can combine them into one object:
import*as React from'react';import{
NavigationContainer,
DarkTheme as NavigationDarkTheme,}from'@react-navigation/native';import{
DarkTheme as PaperDarkTheme,
Provider as PaperProvider,}from'react-native-paper';const CombinedDarkTheme ={...PaperDarkTheme,...NavigationDarkTheme,
colors:{...PaperDarkTheme.colors,...NavigationDarkTheme.colors },};exportdefaultfunctionMain(){return(<PaperProvidertheme={CombinedDarkTheme}><NavigationContainertheme={CombinedDarkTheme}>{/* content */}</NavigationContainer></PaperProvider>);}
If code for themes merging looks complex, you can use a deepmerge package. It will simplify the implementation significantly.
Custom themes
Of course, the built-in themes are not the only themes we can apply. Both libraries allow full customization and you can learn about it in the official documentation (React Navigation, React Native Paper)
In the last step, I want to show you how to change the theme dynamically. We will implement a switch in a drawer that will allow users choosing light or dark theme.
We need to store information about the currently selected theme somewhere. The local state of the root component sounds reasonable. Also, we will conditionally pass different themes based on the state.
import*as React from'react';import{
NavigationContainer,
DefaultTheme as NavigationDefaultTheme,
DarkTheme as NavigationDarkTheme,}from'@react-navigation/native';import{
DarkTheme as PaperDarkTheme,
DefaultTheme as PaperDefaultTheme,
Provider as PaperProvider,}from'react-native-paper';const CombinedDefaultTheme ={...PaperDefaultTheme,...NavigationDefaultTheme,};const CombinedDarkTheme ={...PaperDarkTheme,...NavigationDarkTheme };exportdefaultfunctionMain(){const[isDarkTheme, setIsDarkTheme]= React.useState(false);const theme = isDarkTheme ? CombinedDarkTheme : CombinedDefaultTheme;// Use Light/Dark theme based on a statefunctiontoggleTheme(){// We will pass this function to Drawer and invoke it on theme switch presssetIsDarkTheme(isDark=>!isDark);}return(<PaperProvidertheme={theme}><NavigationContainertheme={theme}>{/* content */}</NavigationContainer></PaperProvider>);}
As you remember, we already render a Switch in a Drawer, but we haven't implemented any logic when it is pressed. Let's take care of it now:
Firstly, we get a current theme using useTheme hook from Paper. This means we can check dark property on it and pass the correct value to Switch.
Secondly, we pass a toggleTheme function to TouchableRipple to toggle theme whenever user presses a Switch.
You should be able to toggle a switch now and both Provider from Paper and NativeNavigationContainer from React Navigation will automatically apply correct colors to the components.
Summary
We all know that UI Component library like Paper can speed up the development, but integrating it with a navigation can be sometimes not very straighforward. I hope I showed you the most important aspects of this process in this guide. After reading this article, using Paper's BottomNavigation, Appbar, Drawer, FAB or Portal alongside with React Navigation shouldn't be a problem for you.
This is a guest post by the UI Kitten team. If you like this guide, checkout UI Kitten for more!
In this blog post, we'll show a step-by-step guide on using React Navigation 5 with UI Kitten.
Introduction
The new React Navigation comes with several significant improvements such as improving animation performance with gesture-handler and reanimated libraries. What's more, it was migrated to TypeScript for improving the quality of your code base with type checking and more. But the biggest update is migrating to component-based API.
Eva Design System is a customizable Design System that is easy to adapt to your brand. It provides Mobile and Web component libraries and allows businesses to quickly create beautiful unique branding themes. The React Native realization of Eva Design System includes UI Kitten, React Native framework for building modern cross-platform mobile applications.
The UI Kitten team started actively using React Navigation alpha and we're proud to announce the full compatibility to the new React Navigation API. In this guide, we won't consider how to implement all of the boilerplate stuff like auth screens. Instead, we will learn how to navigate between screens using Drawer, Bottom Tabs, Top Tabs, and Stack navigators to build a TODO-App. Furthermore, we'll demonstrate using React Navigation with UI Kitten components.
Overview
React Navigation 5 is nothing else rather than simplifying navigation structure in your app.
To create a navigator, you import createXNavigator function from the navigator package of your choice and use Navigator and Screen components from the value it returns.
Unlike the previous React Navigation versions, all the screens used inside a navigator are passed as child elements with wrapping it to a Screen component. If you need to set up additional navigator configuration like configuration of the header, you can simply pass corresponding props directly to the Navigator component.
Getting started
Clone the project from GitHub. It contains all the required source code for the initial setup.
Assuming that your app users will need to authorize before getting to the home screen, we will need to create both Authentication and Home navigators. Then we're going to combine it with simple stack navigation and pick the initial screen depending on the user authorization status.
In this example, we're using a createStackNavigator function to create simple stack navigation between Sign In, Sign Up and Reset Password screens. Under Stack Navigator we mean the default navigation behavior between screens: with slide-from-right animation on iOS, and slide-in-top on Android.
In ./src/navigation/app.navigator.tsx file and replace the placeholder screen with Auth Navigator. This will make authentication screens to be the starter point of your app.
Say, our app has both in-progress and finished tasks. So, you should separate them to avoid a mess. Here you can make it with two tabs on the home screen. To do this, we need to have three screens: two for tabs and one master screen for navigation management between tabs. Unlike the Stack Navigator component, the Top Tabs Navigator has a special prop for the component to control navigation between tabs - tabBar. We will use it to configure the tab bar with UI Kitten components.
The code above will enable you to navigate with gestures between In Progress screen and Done screen, but not set up the Tab Bar. Open ./src/scenes/todo/todo-tab-bar.component.tsx file and paste the following code:
With the code above we render TabBar component with two tabs inside: one per each screen inside TodoNavigator. Then, we use the React Navigation state to pass selectedIndex and onSelect props to navigate between screens. So, when the user taps one of the tabs, the TabBar component calls onTabSelect function and this is the place where we need to navigate to the corresponding route.
Finally, open app.navigator.tsx file and add the TodoNavigator as a Home screen. Now you're able to navigate the home screen with todo tabs after sign in.
Sometimes you may want your app to contain tabs at the bottom. Here is the main semantic difference regarding the tabs at the top: while they should represent the content of the same type, the bottom tabs could be used to show any content of your application. This is where we're going to use createBottomTabNavigator and BottomNavigation.
Let's start by creating another navigator for the second tab. The first one will be used for Todo screens. Open ./src/navigation/profile.navigator.tsx file and paste the following code:
This will add a simple stack navigator, just like we did it for authentication flow.
Now we need to somehow connect TodoNavigator with ProfileNavigator . The implementation is as simple as creating a navigator for top tabs. Thanks to React Navigation, we have totally the same API for this. Open ./src/navigation/home.navigator.tsx file and paste the following code:
Using the code above we render BottomNavigation component with two tabs inside: one per each screen inside HomeNavigator. We use the React Navigation state to pass selectedIndex and onSelect props to navigate between screens. So, when the user taps one of the tabs, the BottomNavigation component calls onSelect function. Well, this is the place where we need to navigate to the corresponding route.
Then, open app.navigator.tsx file and replace the TodoNavigator with HomeNavigator:
At the final stage of this guide, we will describe how to create the drawer navigation. While the top and bottom tabs can be used to present the main product features, a drawer menu can be also used to direct a user to legal information about it, or simply contain quick actions like a logout.
Usually, the drawer menu is available in app on the home screen, so let's add it to HomeNavigator. Open ./src/navigation/home.navigator.tsx file and paste the following code:
In this example, we've implemented a Drawer Navigator with createDrawerNavigator and used it to display on the Home screen. We have also added AboutScreen to demonstrate navigation directly from the Drawer menu.
Just like Top/Bottom tab navigators, the drawer navigator also has a special property for declaring custom drawer view. Use a drawerContent property to pass the custom view to the navigator. Open ./src/scenes/home/home-drawer.component.tsx file and add the following code:
Due to the use of this code, we render Drawer component with two actions inside: one for navigating to legal information screen and one for performing a user logout. Then, we pass data prop to display our actions and onSelect prop to handle it. So, when the user taps the action, the Drawer component calls onMenuItemSelect function and this is the place where we need to handle it.`
The new React Navigation has great TypeScript support and exports type definitions for navigators and custom navigation components. Sometimes you may want to type-check the params you're passing when navigating between routes. You also may want to make autocomplete work when working with navigation props.
Now you can modify props of Auth screens props by adding types to make your autocomplete and IntelliSense work. For more complex examples, consider reading type-checking doc or reviewing complete demo application sources.
Useful links
By the links below, you can find a lot of useful information about UI Kitten and React Navigation 5. The demo application may contain more complex examples. Also, by referring to the app built by the React Navigation team, you can find plenty of useful examples too.
React Navigation steps firmly into the next level and we’re very excited to announce to you something great happening in React Navigation codebase.
Make it custom
From the very beginning of React Navigation we have been following the important motto to make navigation experience customizable in every detail. We, front-end engineers, mobile developers and User Experience lovers, have a deep need to make decisions about each layout component.
Guided by this idea we (well, not necessarily “we” because I haven’t even written a single line of code this time 😄) made architectural decisions, which might appear controversial for developers with the native background.
We have decided to write React Navigation only in JavaScript replicating native animations and interactions. This is the main decision making our solution very different from other React Native navigation libraries (like React Native Navigation by Wix or The Navigation Router by Graham Mendick).
But… why? What’s the purpose of reinventing the wheel?
Firstly, because JavaScript implementation can be more customizable. It’s easy to maintain and develop new features. Additionally, being independent of native code makes our codebase more reliable.
Furthermore, it’s easy not only for maintainers but also for developers using our library. No need for native setup makes it super simple to start developing and integrating with the existing (e.g. brownfield) app. Eventually, a very wide range of customizations makes it a great solution for developers with very high UX requirements.
The above reasons have made React Navigation navigation super popular among a number of developers but…
After years of growing development, we need to admit that we’re not always able to deliver an exactly native-like experience, especially when it comes to stack navigation. We also see a ton of users that use just a few ways of customization React Navigation offers…
Native reveal
…so, we have decided to export native stack (*FragmentTransaction *on Android and *UINavigationController *on iOS) in order to deliver an even better experience.
Thanks to the great work of Krzysztof Magiera for React Native Screens library now we can use truly native components instead of JS replicas.
We believe you will find it useful in your projects and strongly encourage you to get acquainted with our documentation. Things that I’m the most excited about are iOS header animations!
Please, note that we don’t intend this component to be a drop-off replacement for the currently existing stack. There are many benefits of using JavaScript-based stack. Moreover, the range of customization of the native stack is very limited and probably won’t get broader due to the limitations of native API.
Native Stack is available in the new 5.0 alpha API and with Satyajit’s help, I have managed to release the first version of the library a few days ago. It’s in alpha so don’t expect everything to be perfect, but… don’t hesitate to submit a PR and help us grow React Navigation!
In this release, we have removed the navigators from the react-navigation package. The navigators have lived in separate packages for quite a while and you could already use those packages manually, but we still bundled them in the react-navigation package. This made it difficult for us to release significant updates to navigators, because we had to then do a major version release of react-navigation too. By separating the navigator packages there is more freedom to update and improve navigators without any impact on folks that don't use them.
For example, you will find when you install the latest versions of the drawer and tab navigators that the animations are more performant because they use react-native-reanimated to smoothly animate gestures. These have been available in react-navigation-drawer and react-navigation-tabs for several months now but we delayed updating them in react-navigation itself because we did not want to force every user to update.
With this version, you now install the navigators from their respective packages, which means that you can independently update them.
For upgrade instructions, please check the release notes.
If you're using TypeScript, navigator specific types were also removed from the main package. We've mentioned the replacement types in the release notes. But if you are still having problems, please open an issue and let us know.
This is the first release where React Navigation depends on a native module outside of React Native core: it now depends on react-native-gesture-handler. This library provides an excellent set of primitives for leveraging the operating systems’ native gesture APIs and has enabled us to fix a variety of issues with stack and drawer navigators. React Navigation also depends on react-native-screens, but you don’t need to install the native module if you prefer not to use it (we have a blog post coming soon that will explain what react-native-screens is and why you may want to use it, or you can watch this talk by the author of the library).
We didn’t get around to every feature that we wanted to land for this release, but we also didn’t want to hold it up further because we expect that you will want to start using it right away - the release includes performance improvements, bugfixes, ergonomics improvements, some handy new features, and a re-organizing of the internals to improve support for web as a target of React Navigation.
Let’s get started with react-navigation 3.0.
Installation
First, install the library using your favorite package manager: yarn add react-navigation@^3.0.0
Next, install react-native-gesture-handler. If you’re using Expo you don’t need to do anything here, it’s included in the SDK. Otherwise, follow these installation instructions.
Optionally, you can install react-native-screens. If you’re using Expo you don’t need to do anything here, it’s included in SDK 30 and higher. Otherwise, follow the instructions in the README on https://github.com/kmagiera/react-native-screens.
Warning: if you have manually installed any navigators in your project, for example react-navigation-material-bottom-tabs, you will need to update those to a version that is compatible with 3.0.0. In the case of react-navigation-material-bottom-tabs, 1.0.0 is compatible.
Breaking changes
When you first run your app after updating it won’t work because react-navigation@^3 requires you to add an app container to the root navigator. Once you get that in place, you may notice that your navigation options aren’t being applied as you expect - this is due to navigationOptions in navigator configuration being renamed to defaultNavigationOptions. If you use a drawer, you may notice that it feels quicker, but if you depend on inactive screens being unmounted you’ll be surprised. More details on these changes and how to update your app to work just as well (probably better) than before below.
Explicit app container required for the root navigator
In the past, any navigator could act as the navigation container at the top-level of your app because they were all wrapped in “navigation containers”. The navigation container, now known as an app container, is a higher-order-component that maintains the navigation state of your app and handles interacting with the outside world to turn linking events into navigation actions and so on.
This should be an easy change - import createAppContainer in the root of your app and use it to wrap the root navigator.
Warning: if you have any custom navigators, you may have used createNavigationContainer, you can remove this now because it’s only used at the root of the app and provided by the user.
Renamed navigationOptions in navigator configuration
When configuring navigators it’s often useful to pass in default navigation options for the screens inside of that navigator. For example in a stack you might want to set a background color and tint color for each screen. Previously, you would write something like this:
We’re sorry to make you go hunt through your code and rename a handful of strings, the hope is that this change makes the code more readable and more intuitive to new users in the future.
Drawer now keeps inactive tabs in memory by default
Previously when using the drawer navigator screens would unmount when inactive, and when you switch back to them you’d need to re-render the entire thing. In tabs these stay in memory as you would expect, so once you switch to the screen once it’s faster to go back there again and you don’t lose your place in a scroll view or anything. Drawer now behaves the same way, but you can go back to the old behavior if you like by passing in unmountInactiveRoutes: true in the drawer navigation configuration.
react-navigation now exports ScrollView, FlatList, and SectionList that will scroll to top when tapping on the active tab as you would expect from native tab bars.
Drawer supports two more types in addition to the default ‘front’ behavior that you expect from typical Android drawers: back and slide.
You can now provide default params inside of route definitions:
Basic support for hooks in react-navigation-hooks (although this isn't too useful yet without a stable react-native build that supports hooks, you can play with it on the web!).
headerBackgroundTransitionPreset: 'toggle' | 'fade' | 'translate' lets you choose how to transition your custom headerBackground components between screens.
Add options to opt in/out of the stack card overlay and shadow that are visible during transitions: cardShadowEnabled defaults to true and cardOverlayEnabled defaults to false.
Tab navigators support tabBarOnLongPress configuration option, which defaults to the same as the tab press event.
Assorted fixes & improvements
Stack transition performance improved greatly by removing the shadow from the entire card and rendering it only on the slice where it is needed. The card opacity is also no longer directly animated but instead an overlay is put on top to create a similar effect but with better performance.
Stack card gesture uses react-native-gesture-handler and native driver so the gesture runs on the UI thread (except when the gesture ends, then it calls back to JS).
Fix accessibility voiceover for tabs and stack back button.
Ecosystem and web support
React Navigation 3.0 brings some important changes to the React Navigation ecosystem: the project now lives across a number of repositories and packages, we have an exciting new transitioner on the way, and the core finally has first-class support for web apps on the client and server!
Independent Projects
React Navigation has always been a set of loosely-coupled navigation components: Stack, Tabs, Drawer, etc. But until now they have always lived in the main navigation repo, which has been difficult to maintain. People often struggle to use different versions of these components, or they want to fork them for their own app.
In v3, all of our main packages and repos are separated. There are the following core packages in our new NPM org:
@react-navigation/core - The primitives and utilities that define our patterns, plus several routers
@react-navigation/native - Container and support for navigators on React Native apps. createAppContainer from the main react-navigation package actually comes from this package.
@react-navigation/web - Web browser app container, and utilities for server rendering
In addition, we have published our community-maintained components as standalone repos and packages:
react-navigation-stack
react-navigation-tabs
react-navigation-drawer
react-navigation-transitioner
react-navigation-hooks
To keep the experience as simple as possible the react-navigation package will continue to be supported, and will contain most of the above components as it did before.
Web Support
Now that the core of React Navigation can be used outside of React Native, we can provide first-class web support to anyone using React.js on the web, including those who do not want to use react-native-web.
Here is an example web app which demonstrates the new createBrowserApp container and the built-in Link component:
We are excited to announce the release candidate of version 3.0 today!
This is the first release where React Navigation depends on a native module outside of React Native core: it now depends on react-native-gesture-handler. This library provides an excellent set of primitives for leveraging the operating systems’ native gesture APIs and has enabled us to fix a variety of issues with stack and drawer navigators. React Navigation also depends on react-native-screens, but you don’t need to install the native module if you prefer not to use it (we have a blog post coming soon that will explain what react-native-screens is and why you may want to use it, or you can watch this talk by the author of the library).
We didn’t get around to every feature that we wanted to land for this release, but we also didn’t want to hold it up further because we expect that you will want to start using it right away - the release includes performance improvements, bugfixes, ergonomics improvements, some handy new features, and a re-organizing of the internals to improve support for web as a target of React Navigation.
Let’s get started with react-navigation 3.0.
Installation
First, install the library using your favorite package manager: yarn add react-navigation@^3.0.0-rc.0
Next, install react-native-gesture-handler. If you’re using Expo you don’t need to do anything here, it’s included in the SDK. Otherwise: yarn add react-native-gesture-handler && react-native link
Optionally, you can install react-native-screens. If you’re using Expo you don’t need to do anything here, it’s included in SDK 30 and higher. Otherwise, follow the instructions in the README on https://github.com/kmagiera/react-native-screens.
Warning: if you have manually installed any navigators in your project, for example react-navigation-material-bottom-tabs, you will need to update those to a version that is compatible with 3.0.0. In the case of react-navigation-material-bottom-tabs, 1.0.0-alpha.2 is compatible.
Breaking changes
When you first run your app after updating it won’t work because react-navigation@^3 requires you to add an app container to the root navigator. Once you get that in place, you may notice that your navigation options aren’t being applied as you expect - this is due to navigationOptions in navigator configuration being renamed to defaultNavigationOptions. If you use a drawer, you may notice that it feels quicker, but if you depend on inactive screens being unmounted you’ll be surprised. More details on these changes and how to update your app to work just as well (probably better) than before below.
Explicit app container required for the root navigator
In the past, any navigator could act as the navigation container at the top-level of your app because they were all wrapped in “navigation containers”. The navigation container, now known as an app container, is a higher-order-component that maintains the navigation state of your app and handles interacting with the outside world to turn linking events into navigation actions and so on.
This should be an easy change - import createAppContainer in the root of your app and use it to wrap the root navigator.
Warning: if you have any custom navigators, you may have used createNavigationContainer, you can remove this now because it’s only used at the root of the app and provided by the user.
Renamed navigationOptions in navigator configuration
When configuring navigators it’s often useful to pass in default navigation options for the screens inside of that navigator. For example in a stack you might want to set a background color and tint color for each screen. Previously, you would write something like this:
We’re sorry to make you go hunt through your code and rename a handful of strings, the hope is that this change makes the code more readable and more intuitive to new users in the future.
Drawer now keeps inactive tabs in memory by default
Previously when using the drawer navigator screens would unmount when inactive, and when you switch back to them you’d need to re-render the entire thing. In tabs these stay in memory as you would expect, so once you switch to the screen once it’s faster to go back there again and you don’t lose your place in a scroll view or anything. Drawer now behaves the same way, but you can go back to the old behavior if you like by passing in unmountInactiveRoutes: true in the drawer navigation configuration.
New features
react-navigation now exports ScrollView, FlatList, and SectionList that will scroll to top when tapping on the active tab as you would expect from native tab bars.
Drawer supports two more types in addition to the default ‘front’ behavior that you expect from typical Android drawers: back and slide.
You can now provide default params inside of route definitions:
headerBackgroundTransitionPreset: 'toggle' | 'fade' | 'translate' lets you choose how to transition your custom headerBackground components between screens.
Add options to opt in/out of the stack card overlay and shadow that are visible during transitions: cardShadowEnabled defaults to true and cardOverlayEnabled defaults to false.
Stack transition performance improved greatly by removing the shadow from the entire card and rendering it only on the slice where it is needed. The card opacity is also no longer directly animated but instead an overlay is put on top to create a similar effect but with better performance.
Stack card gesture uses react-native-gesture-handler and native driver so the gesture runs on the UI thread (except when the gesture ends, then it calls back to JS).
React Navigation 3.0 brings some important changes to the React Navigation ecosystem: the project now lives across a number of repositories and packages, we have an exciting new transitioner on the way, and the core finally has first-class support for web apps on the client and server!
Independent Projects
React Navigation has always been a set of loosely-coupled navigation components: Stack, Tabs, Drawer, etc. But until now they have always lived in the main navigation repo, which has been difficult to maintain. People often struggle to use different versions of these components, or they want to fork them for their own app.
In v3, all of our main packages and repos are separated. There are the following core packages in our new NPM org:
@react-navigation/core - The primitives and utilities that define our patterns, plus several routers
@react-navigation/native - Container and support for navigators on React Native apps. createAppContainer from the main react-navigation package actually comes from this package.
@react-navigation/web - Web browser app container, and utilities for server rendering
In addition, we have published our community-maintained components as standalone repos and packages:
react-navigation-stack
react-navigation-tabs
react-navigation-drawer
react-navigation-transitioner
react-navigation-hooks
To keep the experience as simple as possible the react-navigation package will continue to be supported, and will contain most of the above components as it did before.
Web Support
Now that the core of React Navigation can be used outside of React Native, we can provide first-class web support to anyone using React.js on the web, including those who do not want to use react-native-web.
Here is an example web app which demonstrates the new createBrowserApp container and the built-in Link component:
The following changes are considered “significant” because they can’t be fixed by search and replace or something that is similarly mechanical.
navigate(routeName) in StackNavigator is “less pushy”
In 1.x, navigate(routeName) and push(routeName) were very similar: every time you called navigate(routeName) it would push a new route to the stack. Now navigate(routeName) will first try to find an existing instance of the route and jump to that if it exists, otherwise it will push the route to the stack.
To update your app for this change you may need to change navigate to push in places where you would like to push a new route every time. Alternatively, you could consider using a key: navigate({routeName: ‘MyRoute’, key: data.uniqueId, params: data}). Read more about navigation with keys.
Previously, push only applied to the deepest active stack router. This meant that if you had Stack A > Stack B and Stack B fired push(‘MyRoute’), even if Stack B does not have a route with the name ’MyRoute’ and Stack A does, the screen would not be pushed. We made this change to accommodate for the “less pushy” navigate behavior.
When updating your app, you may want to double check where you use push to ensure that this doesn’t impact the expected behavior of your app.
A common source of confusion for developers working with React Navigation has been around navigationOptions resolution. For example, if you have a stack navigator with a header, and a drawer inside of that stack, then in some circumstances the title of the stack would change every time you change screens in the drawer. This is because the stack navigator would crawl into child navigators and pull navigationOptions off of the deepest active screen. As of 2.0, this no longer happens: navigators will only look at their direct children for navigationOptions.
It is now easier to create and maintain custom navigators. The new createNavigator API fully decouples the navigation view from the router. Information about each screen is available on a single "descriptor", including the pre-computed child navigation prop, allowing you to focus on custom navigation views.
Custom navigators can now provide actions helpers for their screens. For example, the new drawer navigator now allows props.navigation.openDrawer() within its screen components.
The following changes are considered “trivial” because you will only need make straightforward and mechanical changes to update your app for them.
Drawer routes have been replaced with actions
Rather than opening a drawer with navigation.navigate(‘DrawerOpen’), you can now call navigation.openDrawer(). Other methods are closeDrawer() and toggleDrawer(). See pull 3618.
Navigation actions API overhaul
In practice, this change is unlikely to impact your app at all except for one case that is mentioned below.
In 1.x, functions on the navigation were not contextual - they would be the same regardless of whether your screen was inside of a drawer, a stack, a tab navigator, etc. In 2.0 the functions that are available to you on the navigation prop depend on the navigators that it corresponds to. If your screen is inside of both a stack and a drawer navigator, you will have helpers for both -- push and openDrawer, for example.
Given that we only exposed generic helpers (navigate, goBack) and helpers specific to the stack in 1.x, this would only impact you if you attempted to use the stack helpers from outside of a stack. For example, if you had a tab navigator with a stack in tab A and just a plain screen in tab B, then tried to push a route from the screen in tab B, push would not be available. Keep this in mind when you update your app if it follows this type of structure.
One of the big improvements you get from this is that you can now add your own helpers to the navigation prop! Read more in RFC 6 and in pull 3392.
NavigationActions no longer have toString() implementations (related)
This change was intended to simplify the implementation of actions. We may go back on this, however, and apologize in advance if this thrasing causes you trouble.
NavigationActions split up according to router
If you are using NavigationActions.push or other stack-specific actions, you’ll need to import StackActions and use StackActions.push instead.
Deprecations
XNavigator(...) is now createXNavigator(...)
StackNavigator, TabNavigator and DrawerNavigator are now deprecated in favour of createStackNavigator, createTabNavigator, and createDrawerNavigator, which are functionally identical but more clearly communicate that they are functions and that they return a component. The XNavigator style will removed in 3.0.
Tab navigator split into separate components
Previously, TabNavigator would render a navigation bar on the top of the screen on Android and the bottom on iOS. We’ve now pulled these navigators apart, so you can use createBottomTabNavigatorand createMaterialTopTabNavigator explicitly depending on what you need. You can use createTabNavigator to have the same behavior as before, but it will be removed in 3.0.
It is worth noting additionally that createBottomTabNavigator is different from the bottom tab navigator that is available through TabNavigator in that it does not support animationEnabled or swipeEnabled properties.
Enhancements
dangerouslyGetParent and dismiss helpers on navigation prop (3669)
State persistence - automatically save state and reload it when the app restarts (3716)
Smoothly transition header visibility in Stack (3821)
SwitchRouter now returns null on idempotent navigation (commit).
Final notes
The breaking changes and deprecations in this release resolve a lot of issues that users have encountered that have been perceived as bugs but were technically expected behavior. We think that we’ve drastically improved library ergonomics for new users and experienced users alike, let us know on Twitter what you think.
Exactly two months after the release of React Navigation 1.0, we are close to another major version release. We’d like to invite developers that use the library to give the release candidate a try in your app and let us know if you encounter any issues.
We’re bumping the major version because some of the changes in this release are backwards incompatible. That said, this should be a fairly easy upgrade. We are improving React Navigation incrementally because we don't want to leave developers feeling stranded in an old version. If you use React Navigation in a conventional way and don't have any custom navigators, I can't imagine this update would take you more than an hour.
This blog post is not a comprehensive changelog - that will come with the 2.0 proper release; the following is a list of the breaking changes, suggestions for how you can update your app to accommodate them, notice of deprecations, and some of my favourite new features.
Breaking changes
navigate(routeName) in StackNavigator is “less pushy”
In 1.x, navigate(routeName) and push(routeName) were very similar: every time you called navigate(routeName) it would push a new route to the stack, regardless. Now navigate(routeName) will first try to find an existing instance of the route and jump to that if it exists, otherwise it will push the route to the stack.
To update your app for this change you may need to change navigate to push in places where you would like to push a new route every time. Alternatively, you could consider using a key: navigate({routeName: ‘MyRoute’, key: data.uniqueId, params: data}). Read more about navigation with keys.
Previously, push only applied to the deepest active stack router. This meant that if you had Stack A > Stack B and Stack B fired push(‘MyRoute’), even if Stack B does not have a route with the name ’MyRoute’ and Stack A does, the screen would not be pushed. We made this change to accommodate for the “less pushy” navigate behavior.
When updating your app, you may want to double check where you use pushto ensure that this doesn’t impact the expected behavior of your app.
Shallow navigation options
A common source of confusion for developers working with React Navigation has been around navigationOptions resolution. For example, if you have a stack navigator with a header, and a drawer inside of that stack, then in some circumstances the title of the stack would change every time you change screens in the drawer. This is because the stack navigator would crawl into child navigators and pull navigationOptions off of the deepest active screen. As of 2.0, this no longer happens: navigators will only look at their direct children for navigationOptions. Read more about this in RFC 5.
New API for creating navigators
This does not impact most users, but if you have any custom navigators in your app, read on. Read more about the changes in RFC 2. Also read the custom navigators documentation.
Drawer routes have been replaced with actions
Rather than opening a drawer with navigation.navigate(‘DrawerOpen’), you can now call navigation.openDrawer(). Other methods are closeDrawer() and toggleDrawer().
Navigation actions API overhaul
In 1.x, functions on the navigation were not contextual - they would be the same regardless of whether your screen was inside of a drawer, a stack, a tab navigator, etc. In 2.0 the functions that are available to you on the navigation prop depend on the navigators that it corresponds to. If your screen is inside of both a stack and a drawer navigator, you will have helpers for both -- push and openDrawer, for example.
Given that we only exposed generic helpers (navigate, goBack) and helpers specific to the stack in 1.x, this would only impact you if you attempted to use the stack helpers from outside of a stack. For example, if you had a tab navigator with a stack in tab A and just a plain screen in tab B, then tried to push a route from the screen in tab B, push would not be available. Keep this in mind when you update your app if it follows this type of structure.
One of the big improvements you get from this is that you can now add your own helpers to the navigation prop! Read more in RFC 6.
Deprecations
The following APIs are deprecated and will be removed in 3.0.
XNavigator is now named createXNavigator
import { createStackNavigator } from ‘react-navigation’;
createStackNavigator({routeName: Screen});
This change was made to improve the ease of learning and understanding the library. The navigator constructors are functions that return components (HOCs), and that was not previously very well communicated by the name.
TabNavigator has been split up into more focused navigators
TabNavigator (now createTabNavigator as per above) was a frequent source of confusion for users because it would use a bottom tab bar on iOS and a top tab bar on Android by default. Additionally, some of the configuration properties applied to the bottom tab bar, and others to the top tab bar. The equivalent components are now: createBottomTabNavigator and createMaterialTopTabNavigator. We’ve also introduced a new type of tab navigator, createMaterialBottomTabNavigator - a material design styled bottom tab bar based navigator from react-native-paper. Thank you satya164 for your great work on this!
As of today, React Navigation version 1.0 has been released! To see a list of recent improvements leading up to the release, refer to the “Renewed Path to React Navigation V1.0” issue. Thank you to everybody who has filed high-quality bug reports, submitted pull requests, and helped out fellow developers while we were in beta!
This release isn’t meant to be interpreted as a signal that React Navigation is “finished" — work is already underway on a number of significant changes to the API that aim to make React Navigation easier to use and more powerful. These changes will be run through our new RFC process. For example, the Navigator View API RFC outlines a plan to decouple views from the router in order to make it easier for other navigation APIs, such as react-native-router-flux and react-router-native, to build on top of the same navigation views. We’re also very excited about a plan (no RFC is open yet, but keep an eye out for it) to use react-native-gesture-handler to run navigation gestures (sliding a drawer open, or swiping back on a stack, for example) on the UI thread using the Animated native driver.
Last week we released an updated version of our documentation website, built using Docusaurus. We hope that the new documentation will help newcomers get onboarded with the fundamentals of React Navigation, and serve as an ongoing resource to handle specific use cases as you come across them (under the “How do I do..?” section). Contributions to the documentation are very welcome.