You want to switch to React Native , but you have an existing native app? Should you trash it? Of course not!

At BAM, we integrated React Native inside two native apps, both in production with a few millions users. There is seldom documentation on the matter, so you’ll learn about some challenges we faced and how to solve them.





This article will deal with the first challenge we had to face: the navigation. But Before diving into the technical details, you might be wondering why you would switch to React Native.

Why React Native

Here are the main advantages:

One single codebase

One of the key selling points is that you develop one single code for both iOS and Android.

Better build times

When editing your code, reloading the app to see your changes takes only a few seconds. No need to wait for the Gradle build to finish anymore!

Plus, you can benefit from features similar to those available to web developers, like hot reloading:

React Native is essentially native

React Native consists of Javascript code controlling Native UI.

An <Image /> React component will display a native UIImage on iOS or a native ImageView on Android.

So React Native is essentially Native! If you open your React Native app in the XCode hierarchy inspector or the Android Studio one, you'll indeed see the Native UI components:

That's why React Native is an adequate solution to keep performance and UX consistent with the native app.

The navigation challenge

Why we use native-navigation

The first challenge that we need to tackle is how to handle navigation.

When using React Native, the officially recommended solution for navigating between screens is react-navigation. But it’s a full javascript solution so that’s not a good fit here.

Let me explain, we need to keep a consistent navigation stack, right?

For instance, you might want to have a hybrid navigation stack like so:

your app opens with a native screen

you push a native screen

then you push a React screen

then push another React screen from it

then let's say from this React screen, you want to push back to a native screen.



We therefore need a solution aware of the whole navigation stack.

Plus, we also want to keep a consistent look and feel throughout all the app. The user should not know whether he is seeing React Native components or pure native components.

Lucky us! Airbnb is developing a great Open source solution with that exact goal in mind: integrating React Native screens inside their apps. This solution is Native Navigation.

Since they're not actively maintaining it, we forked it to @bam.tech/native-navigation and added a few features (such as XCode 9 support and iPhone X support).

To install it, checkout the installation guide.

Push some React Native screens!

Once installing native-navigation is done, it is fairly simple to manage the navigation stack:





Register your screens in Javascript:





import Navigator from " native-navigation " ; import ArticleDetailScreen from " ./screens/ArticleDetail " ; Navigator . registerScreen ( " ArticleDetail " , () => ArticleDetailScreen);

You can now push your RN views from the native side: in Swift for iOS





import NativeNavigation ... let screen = ReactViewController ( moduleName : " ArticleDetail " , props : [ " articleId " : articleRef. identifier as AnyObject ], ) navigationController ? . pushReactViewController (screen, animated : true )

in Java for Android

final Bundle props = new Bundle (); props . putString( " articleId " , article . getId()); screenCoordinator . pushScreen( " ArticleDetail " , props);

Or push React Native views from the JavaScript side

Navigator . push ( " ArticleDetail " , { articleId : article . id });

Use a native module to push native screen from React Native

For instance on iOS:

import NativeNavigation @objc ( NativeRedirector ) class NativeRedirector : NSObject { @objc ( openNativePage: ) func openTeamPage ( options : NSDictionary) -> Void { let viewController = NativeViewController. instantiate () if let navController = ReactNavigationCoordinator. sharedInstance . topNavigationController () { // Native React Native bridges are executed in a separate thread // You need to dispatch in the main thread to push a view controller DispatchQueue. main . async ( execute : {() -> Void in navController. pushViewController (viewController, animated : true ) }) } } }

Customizing the native navigation bar in Javascript

To ensure a consistent navigation between purely native and React Native, we need to make sure the navigation bar and its transitions in React Native match what the native side does.

Fortunately, the navigation bar with native-navigation is actually native! It should be pretty familiar to native developers:





On iOS, it's the usual UINavigationBar attached to any UIViewController

attached to any On Android, it's a ReactToolbar class, extending the Android Toolbar





The other good news is you can customize it for your screens, from the Javascript side with Navigator.Config . It's the React Native way, it's a Javascript component controlling Native UI.

You can checkout here the list of options available.





import Navigator from " native-navigation " ; import ArticleDetailScreen from " ./screens/ArticleDetail " ; Navigator . registerScreen ( " ArticleDetail " , () => ( < Navigator . Config translucent statusBarTranslucent elevation = { 5 } rightButtons = {[ { title : " Right button " } ]} onRightPress = {() => alert ( " button clicked " )} > < ArticleDetailScreen / > < / Navigator . Config > ));

Remember our navigation stack from earlier, we can do it now! 🎈



Here's an example of the Kickstarter iOS app hijacked by React Native:





With its React Native code:

import React from " react " ; import { Button , NativeModules , Text , View } from " react-native " ; import Navigator from " @bam.tech/native-navigation " ; Navigator . registerScreen ( " RNScreen2 " , () => () => ( < Navigator . Config title = " RN Again " backButtonTitle = " " > < View style = > < Button title = " Open native screen " style = onPress = {() => NativeModules . NativeRedirector . openTeamPage ({})} / > < / View > < / Navigator . Config > )); Navigator . registerScreen ( " RNScreen " , () => () => ( < Navigator . Config title = " React Native! " backButtonTitle = " " > < View style = > < Text style = > BRO < / Text > < Text style = > Do you even React ? < / Text > < Button title = " Open RN Screen " onPress = {() => Navigator . push ( " RNScreen2 " )} / > < / View > < / Navigator . Config > ));

Questions

Do you have any other issue in mind about integrating React into existing native apps? Feel free to tell us what you want to hear about next in the comments! :)