Make professional mobile apps with React Native and Typescript — Routing and navigation (Chapter 3 — Part 2)

Thinh Tran
5 min readApr 13, 2020

Routing and navigation with react-navigation.

Photo by Malcolm Lightbody on Unsplash

For a production app, it's very rare that it consists of only one screen but is made of multiple screens. To navigate between screens, we use stack-based navigation, tab-based navigation, drawer-based navigation or a combination of them. React Native doesn't support it natively. Instead, we use a 3rd library to solve the problem. There are 2 libraries which are officially recommended: react-navigation & react-native-navigation.

Pro tip: If you start to make a new app, react-navigation is the way to go. It provides cleaner & easier APIs to work with (and better documentation). Previously, the performance was an obstacle when working with large applications, but the recent versions resolved it. You should choose react-native-navigation if you want a native look and feel or want to integrate React Native into an existing app which already has navigation natively.

In this series, we'll go with react-navigation. First, clone the repository or switch to the branch setup_linting_tools (if you already walk through previous parts).

git clone -b setup_linting_tools https://github.com/thinhtran3588/react-native-starter-kit.git

Then install react-navigation (5.1.5 at the time of writing this) and its dependencies.

yarn add @react-navigation/native
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

Then add import ‘react-native-gesture-handler’; into the index.js file in the root folder.

index.js

Now let's implement tab-based navigation. Install @react-navigation/bottom-tabs

yarn add @react-navigation/bottom-tabs

The modify your src/app.tsx as below

app.tsx

Run yarn ios or yarn android to start your app. Now your app already has 2 tabs Weather & Settings

navigation screenshots

We're missing some icons here. Let's install react-native-vector-icons , a very popular library with many built-in vector icon sets. Run

yarn add react-native-vector-icons
yarn add --dev @types/react-native-vector-icons

to install it and its types. Then open ios/mobile/Info.plist and add below lines inside the dict tag.

<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
</array>

For Android, open android/app/build.gradle and add the following line (after apply plugin: “com.android.application” )

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

The last step is updating your app.tsx

app.tsx

Now run yarn ios and yarn android rebuild your iOS app since it requires a new native library. You get nice icons in the bottom tab now.

bottom tab with icons

Congratulations! You've already successfully integrated navigation with pretty icons!

You should notice by now that all the code is in the app.tsx file. Now, it’s time to break it into smaller components with the structure we discussed in the previous part.

First, we need to make some preparations for pretty import statements. Open .eslintrc.js and update the custom rule import/no-unresolved to

'import/no-unresolved': ['error', { ignore: ['@app', '@core', '@settings', '@weather'] }], // ignore module import

Then add a new custom rule.

'react/jsx-closing-bracket-location': 'off', // let prettier formats the code

Open babel.config.js and update alias.

alias: {   '@app': './src',   '@core': './src/core',   '@weather': './src/modules/weather',   '@settings': './src/modules/settings',},

Open ts.config and update path.

"paths": {   "@app/*": ["src/*"],   "@core/*": ["src/core/*"],   "@weather/*": ["src/modules/weather/*"],   "@settings/*": ["src/modules/settings/*"]},

Now, we'll make an interface for navigation items. Create the folder src/core/interfaces (of course, you have to make the parent folders first) then add index.ts & nav_item.ts.

interfaces

Pro tip: Normally, using the any type is strictly prohibited. However, in some special cases, when making general interfaces/core components, we may have to use it.

Pro tip: Using index.ts to combine components, functions, interfaces in a same root reduces import lines in other files. For example, instead of using:
import {Interface2} from ‘@core/interfaces/interface2’;
import {Interface2} from ‘@core/interfaces/interface2’;
We can use:
import {Interface1, Interface2} from ‘@core/interfaces’;

Now, make some new components. Create the folder src/core/components. Inside it, create a new icon folder, then create a new icon.ts file in that icon folder. Same for src/core/components/navigation/navigation.ts. Then we create a new index.ts in the src/core/components folder

components

Pro tip: We reuse the Icon component from react-native-vector-icons by import & export it without touching it. This is a way of wrapping 3rd components inside our own ones, even though we don't modify it. As a result, in the future, if we want to change it or updating its behavior, we only need to fix it in 1 place. The screen components and their sub-components in modules should never import 3rd components directly but only using @core/components.

Next, create the folder src/modules. Inside it, create 2 folders: settings & weather. In the settings folder, create a screens folder. Inside the screens folder, create a new settings_screen folder that contains settings_screen.ts. You also need to create an index.ts file in the screens folder.

settings

Do the same steps to src/modules/weather/screens/weather_screen/weather_screen.tsx

weather

Note that we import Text and SafeAreaView from ‘@core/components’, not from ‘react-native'. The last part is removing unused app.styles.ts and update app.tsx.

Now, reload the app. You should see the same screens as before. However, the code is much cleaner and well-structured now. Next time, when adding a new tab, we only need to make corresponding modules/screens and add a new nav item.

You can clone the sample here:

git clone -b navigation https://github.com/thinhtran3588/react-native-starter-kit.git

Things you have learned in this part:

  1. make tab-based navigation with react-navigation.
  2. integrate vector icons with react-native-vector-icons.
  3. structure your codebase in a real app.

--

--

Thinh Tran

Software developer. Interested in web/mobile application development with React, React Native, Typescript, Nodejs and AWS.