Getting Started with React Native Navigation V1

Author

Spencer Carli

Developer, cat dad, and devout pizza lover. Teaching at React Native School and building apps with Handlebar Labs.

0

There are quite a few options out there for Navigation in React Native. I typically default to React Navigation (tutorial on getting started) but that’s a Javascript based routing solution. This might not work for some people and others may just prefer a native option.

When React Native was first released the only navigation solution was NavigatorIOS, but it’s no longer being maintained and it only worked on iOS. Airbnb has native navigation, but it’s still very new.

That’s where React Native Navigation comes in — it uses native navigators on iOS and Android and has an easy to use Javascript API. I’ve never used it before but thought I would share my experiences getting up and running with it.

The final code is available on Github.

This tutorial applies to V1 of React Native Navigation. If you’re using V2 the API may have changed.

Prefer Video?

Installation

To get started I’ll create a new React Native project react-native init GettingStartedReactNativeNav. I’m then going to scaffold the application with a few screens, which you can find here.

Then install the package npm install --save react-native-navigation.

iOS

If anything doesn’t work/make sense please check the official documentation.

Open the project in Xcode open ios/GettingStartedReactNativeNav.xcodeproj/. Then, since this uses native libraries, you need to link the native dependencies. First right click on “Libraries” in the project navigator and click “Add files to GettingStartedReactNativeNav…”

1

And select ReactNativeNavigation.xcodeproj, which can be found at nodemodules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj._

Now in the “Build Phases” tab (visible in top navbar of Xcode) in the “Link Binary with Libraries” tab add libReactNativeNavigation.a.

2

Next go to the “Build Settings” tab and search for “Header Search Paths”

3

and add $(SRCROOT)/../node_modules/react-native-navigation/ios. Make sure to set it as “recursive”.

4

Now we need to modify the AppDelegate.m.

AppDelegate.m

**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
+ #import "RCCManager.h"
#import <React/RCTRootView.h>

@implementation AppDelegate

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

+  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
+  self.window.backgroundColor = [UIColor whiteColor];
+  [[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation];

-  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
-                                                      moduleName:@"GettingStartedReactNativeNav"
-                                               initialProperties:nil
-                                                   launchOptions:launchOptions];
-  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

-  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
-  UIViewController *rootViewController = [UIViewController new];
-  rootViewController.view = rootView;
-  self.window.rootViewController = rootViewController;
-  [self.window makeKeyAndVisible];

  return YES;
}

@end

More information can be found here.

Android

Open up android/settings.gradle and add the following to it.

settings.gradle

include ':react-native-navigation'
project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')

Then, in android/app/build.gradle, update the dependencies with compile project(':react-native-navigation'). You also want to update compileSdkVersion and buildToolsVersion, located in android.

build.grade

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.1"
    ...
}

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    compile "com.facebook.react:react-native:+"
    compile project(':react-native-navigation') // ADD DEPENDENCY
}

Next modify MainActivity.java

MainActivity.java

package com.gettingstartedreactnativenav;

- import com.facebook.react.ReactActivity;

- public class MainActivity extends ReactActivity {
-    /**
-     * Returns the name of the main component registered from JavaScript.
-     * This is used to schedule rendering of the component.
-     */
-    @Override
-    protected String getMainComponentName() {
-        return "GettingStartedReactNativeNav";
-    }
-}

+import com.reactnativenavigation.controllers.SplashActivity;

+public class MainActivity extends SplashActivity {
+
+}

We’ve also got to modify MainApplication.java.

MainApplication.java

package com.gettingstartedreactnativenav;

import android.app.Application;

-import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
+import com.reactnativenavigation.NavigationApplication;

import java.util.Arrays;
import java.util.List;

-public class MainApplication extends Application implements ReactApplication {
+public class MainApplication extends NavigationApplication {

  ...
}

Then add the following to the body of MainApplication.java

MainApplication.java

// ...
import javax.annotation.Nullable;

public class MainApplication extends NavigationApplication {
   //  ...

    @Override
    public boolean isDebug() {
        return BuildConfig.DEBUG;
    }

    @Nullable
    @Override
    public List<ReactPackage> createAdditionalReactPackages() {
        return null;
    }
}

Usage

React Native Navigation changes a bit of how React Native works (as you can tell by the installation). The biggest difference is how we register the application. We no longer use AppRegistry.registerComponent so we’ll swap out the index.ios.js and index.android.js with the following

index.js

import registerApp from './app/index';

registerApp();

Then in app/index.js we’ll create our function that actually registers the application.

app/index.js

import { Navigation } from 'react-native-navigation';

import Screen1 from './screens/Screen1';
import Screen2 from './screens/Screen2';
import Screen3 from './screens/Screen3';
import Screen4 from './screens/Screen4';

export default () => {};

The first thing we want to do in that function is register our screens with React Native Navigation. We’ll do that via Navigation.registerComponent.

app/index.js

import { Navigation } from 'react-native-navigation';

import Screen1 from './screens/Screen1';
import Screen2 from './screens/Screen2';
import Screen3 from './screens/Screen3';
import Screen4 from './screens/Screen4';

export default () => {
  Navigation.registerComponent('Screen1', () => Screen1);
  Navigation.registerComponent('Screen2', () => Screen2);
  Navigation.registerComponent('Screen3', () => Screen3);
  Navigation.registerComponent('Screen4', () => Screen4);
};

With that we can use our newly registered screens with the app. We’re going to set up a tab based app in this tutorial — which we do via Navigation.startTabBasedApp. Inside of that config we can pass a “tabs” array which represents the tabs of our application. All of the keys are pretty self explanatory so I won’t cover them. Full documentation is available here. The only thing I’ll note is that screen must align with a screen we registered previously.

Note: I’ve added a few images to the app. You can get those in the Github repo.

index.js

export default () => {
  //   ...

  Navigation.startTabBasedApp({
    tabs: [
      {
        label: 'One',
        screen: 'Screen1',
        icon: require('./images/icon1.png'),
        selectedIcon: require('./images/icon1_selected.png'),
        title: 'Screen One',
      },
      {
        label: 'Two',
        screen: 'Screen2',
        icon: require('./images/icon2.png'),
        selectedIcon: require('./images/icon2_selected.png'),
        title: 'Screen Two',
      },
    ],
  });
};

That leaves you with

null

Navigating Between Screens

To push a new screen onto the stack is very simple. In screens that were registered with Navigation you have access to this.props.navigation on which you simply want to “push” a new screen to it.

Screen1.js

// ...

class Screen extends Component {
  handlePress = () => {
    this.props.navigator.push({
      screen: 'Screen3',
      title: 'Screen 3',
    });
  };

  render() {
    return <Container backgroundColor="#F44336" onPress={this.handlePress} />;
  }
}
null

Modal

Opening a modal is as easy as pushing a new screen onto the stack.

Screen3.js

// ...

class Screen extends Component {
  handlePress = () => {
    this.props.navigator.showModal({
      screen: 'Screen4',
      title: 'Screen 4',
    });
  };

  render() {
    return <Container backgroundColor="#067a46" onPress={this.handlePress} />;
  }
}

export default Screen;
null

In Conclusion

I’ve just scratched the surface of React Native Navigation and I’m excited to learn more about it. There are certainly benefits to going the native route, though installation can be a pain. I’m interested to see how flexible it is and what kind of interactions we can use with it. Until next time!