Log In

Routing with React Native


by Yariv Katz

In this article I want to try combining React native with react-router, and to achieve routing in my react native app.

Bootstrapping react native

Let's start by creating a new react native project. We will use the react-native cli to bootstrap our app.

> npx react-native-cli init RnRouting

You can now launch your app on the simulator by running:

> npm start
> react-native run-ios

React Router

We will use npm to install react router.

> npm install react-router-native --save

NativeRouter

Any routing placed in your app has to be inside the NativeRouter. Modify the file App.js and wrap it with the NativeRouter.

import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';
import { NativeRouter } from 'react-router-native';

type Props = {};
export default class App extends Component<Props> {
    render() {
        return (
        <NativeRouter>
            <View style={styles.container}>
            </View>
        </NativeRouter>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    }
});

Dynamic routing

React routing is dynamic, this means that you don't have to configure an array of static routes, rather you can place Route components that will be shown on the proper routes. You can place those components whereever you want on your page. The app we will create will consist of 2 pages, a home page and an about page. Create a folder components In that folder create the file Home.js with the following:

import React from 'react';
import {View, Text} from 'react-native';

export default function Home(props) {
    return (
        <View>
            <Text>Hello home page</Text>
        </View>
    )
}

In the components folder create another file called About.js with the following code:

import React from 'react';
import {View, Text} from 'react-native';

export default function About(props) {
    return (
        <View>
            <Text>Hello About page</Text>
        </View>
    )
}

Now we would like to show the home component on the homepage, and the about when navigating to the about page. using react router we have to place a route selectors that will display a component based on the current route. We will place a Switch selector that will display either the home or about page. Modify the file App.js:

export default class App extends Component<Props> {
    render() {
        return (
        <NativeRouter>
            <View style={styles.container}>
            <Switch>
                <Route exact path="/" component={Home} /> 
                <Route path="/about" component={About} /> 
            </Switch>
            </View>
        </NativeRouter>
        );
    }
}

So our App component will select to render either the home or the about (or none of them)

Navigating

Let's add navigation from the home component to the about component. We will add 2 types of navigation:
- Link
- Navigation with code by pressing a button.
Modify the file Home.js

import React from 'react';
import {View, Button, Text} from 'react-native';
import { Link } from 'react-router-native';

export default function Home(props) {
    const gotoAbout = () => {
        props.history.push('/about');
    }
    
    return (
        <View>
            <Link to="/about">
                <Text>About</Text>
            </Link>
            <Button onPress={gotoAbout} title="Go to about" />
        </View>
            
    )
}

When the router is rendering a component it will send in the props the history object which we can use the push method to transition to another route. You can now activate the app and you can transition from the home screen to the about screen by using the link of the button. Let's create some styles for our app. We will make each page take the entire screen height and width, and we will color each page in a different color. Create a file in the root directory called styles.js with the following:

import { StyleSheet } from 'react-native';

export const appStyles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'flex-start',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    }
});

export const routePage = StyleSheet.create({
    page: {
        flex: 1,
        width: '100%'
    },
    center: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    }
});

export const homePage = StyleSheet.create({
    bg: {
        backgroundColor: 'red'
    }
});

export const aboutPage = StyleSheet.create({
    bg: {
        backgroundColor: 'yellow'
    }
});

Now let's modify our components to use those styles. Modify the App.js:

import React, {Component} from 'react';
import {View} from 'react-native';
import { NativeRouter, Link, Route, Switch } from 'react-router-native';
import Home from './components/Home';
import About from './components/About';
import {appStyles} from './styles';

type Props = {};
export default class App extends Component<Props> {
    render() {
        return (
        <NativeRouter>
            <View style={appStyles.container}>
            <Switch>
                <Route exact path="/" component={Home} /> 
                <Route path="/about" component={About} /> 
            </Switch>
            </View>
        </NativeRouter>
        );
    }
}

We simply imported our app styles and connected it to the view. The container styles is starting the flex view from the top. Modify Home.js

import React from 'react';
import {View, Button, Text} from 'react-native';
import { Link } from 'react-router-native';
import {routePage, homePage} from '../styles';

export default function Home(props) {
    const gotoAbout = () => {
        props.history.push('/about');
    }

    return (
        <View style={[routePage.page, homePage.bg]}>
            <View style={routePage.center}>
                <Link to="/about">
                    <Text>About</Text>
                </Link>
                <Button onPress={gotoAbout} title="Go to about" />
            </View>
        </View>
            
    )
}

With the flex layout and our view set to flex: 1 while being the only view, this will make the page view to take the whole screen. the inner content view of the page is centered in the middle, and the background of the homepage is set to red. Now modify the About.js

import React from 'react';
import {View, Text} from 'react-native';
import {routePage, aboutPage} from '../styles';

export default function About(props) {
    return (
        <View style={[routePage.page, aboutPage.bg]}>
            <View style={routePage.center}>
                <Text>Hello About page</Text>
            </View>
        </View>        
    )
}

Once again we set this view to take the entire screen and the center view to be placed in the center.

Back

Let's create a transition from the about page back to the homepage. We will create a header with the title of the page and a back button. The back button will appear in the about page but not in the homepage. For this we will use the React Native Elements which contains ui elements for react native. Install the library with npm:

> npm install --save react-native-elements
> npm install react-native-vector-icons --save
> react-native link react-native-vector-icons

In this UI library there is a Header component. Let's add that component in the Home.js and About.js. Modify the file Home.js:

import React from 'react';
import {View, Button, Text} from 'react-native';
import { Link } from 'react-router-native';
import {routePage, homePage} from '../styles';
import { Header } from 'react-native-elements';

export default function Home(props) {
    const gotoAbout = () => {
        props.history.push('/about');
    }

    return (
        <View style={[routePage.page, homePage.bg]}>
            <Header centerComponent={ {text: 'Home', style: {color: '#fff'} } } />
            <View style={routePage.center}>
                <Link to="/about">
                    <Text>About</Text>
                </Link>
                <Button onPress={gotoAbout} title="Go to about" />
            </View>
        </View>
            
    )
}

Now for the about page, let's add a header with a back button. Modify the file About.js:

import React from 'react';
import {View, Text, Button} from 'react-native';
import {routePage, aboutPage} from '../styles';
import {Header} from 'react-native-elements';

export default function About(props) {
    const goBack = () => {
        const {history} = props;
        history.goBack();
    }

    return (
        <View style={[routePage.page, aboutPage.bg]}>
            <Header 
                leftComponent={ 
                    {
                        icon: 'chevron-left',
                        onPress: goBack,
                    }
                }
                centerComponent={ {text: 'About', style: {color: '#fff'} } }
                />
            <View style={routePage.center}>
                <Text>Hello About page</Text>
            </View>
        </View>        
    )
}

We can also add on the Header component an icon in the left button and an action when pressing it. On the action we are grabbing the history and moving back to the home page.

Transitions

Animation is an essential part of a mobile app. Everything needs to be fast and ui friendly and transitions between screens should also be animated properly. We would like the transition between the screens to be with more animation and not just jump to the next screen. Let's examine how this can be accomplished using react-router. There is a library we can install called: react-router-native-stack Which can give us the cool transition effect between our pages. Install the library using npm:

> npm install react-router-native-stack --save

Now we can modify the App.js and replace the Switch with the Stack which will add a navigation control transitions to our app. Change App.js

import React, {Component} from 'react';
import {View} from 'react-native';
import { NativeRouter, Route, Switch } from 'react-router-native';
import Home from './components/Home';
import About from './components/About';
import {appStyles} from './styles';
import Stack from 'react-router-native-stack';

type Props = {};
export default class App extends Component<Props> {
    render() {
        return (
        <NativeRouter>
            <View style={appStyles.container}>
            <Stack>
                <Route exact path="/" component={Home} /> 
                <Route path="/about" component={About} /> 
            </Stack>
            </View>
        </NativeRouter>
        );
    }
}

Now if we run our app and click to move to the about or click back you will see a cool transition between the pages.

Passing route params

The last topic in our agenda is passing route params. We can use the router to transfer information between routes. A useful case will be if we have a list of items taken from a database, with a primary key. Click on a list item will transition to a list detail page. so we might want to transition to the new route while passing the item id to the next screen. Let's use the home component to transfer a message to the about page when transitioning. Modify the App.js with updated route for the About component:

<Route path="/about/:msg" component={About} /> 

Now on the home component modify the component so the link and the button press will pass a different message each. Home.js

export default function Home(props) {
    const gotoAbout = () => {
        props.history.push('/about/button-pressed');
    }

    return (
        <View style={[routePage.page, homePage.bg]}>
            <Header centerComponent={ {text: 'Home', style: {color: '#fff'} } } />
            <View style={routePage.center}>
                <Link to="/about/link-pressed">
                    <Text>About</Text>
                </Link>
                <Button onPress={gotoAbout} title="Go to about" />
            </View>
        </View>
            
    )
}

When clicking the transition button we will send the text: button-pressed
and when the link is pressed we will send the text: link-pressed
Let's modify the About.js to grab that message and display it:

export default function About(props) {
    const goBack = () => {
        const {history} = props;
        history.goBack();
    }
    const msg = props.match.params.msg
    return (
        <View style={[routePage.page, aboutPage.bg]}>
            <Header 
                leftComponent={ 
                    {
                        icon: 'chevron-left',
                        onPress: goBack,
                    }
                }
                centerComponent={ {text: 'About', style: {color: '#fff'} } }
                />
            <View style={routePage.center}>
                <Text>Hello About page</Text>
                <Text>{msg}</Text>
            </View>
        </View>        
    )
}

react-router will send an additional match object in the props, describing the match found and cause this transition. In that object there is the params property with the params passed. From there we are grabbing the msg param and displaying it.

Summary

Even though we are using React Native, we can still use the great react router, with the dynamic routing we love and use on our react apps. Cross platform routing and different component pages has never been easier.