As a React Native developer, you may be asked to implement features like redirecting to a web page or a different app within your React Native app. This can be achieved by using two modules from the React Native API: WebViews and Linking.

The WebView component in React Native core first became available in React Native version 0.57.x. Right now it is available as a community module and is often a default way to let the user visit external links within an iOS or an Android application.

The Linking module allows you to handle deep links that can lead to other applications or use a native browser of the device. We'll show you both the WebView component and the Linking module in action below.

Prerequisites

To follow this tutorial, make sure you have the following installed:

  • NodeJS (>=8.x.x) with npm or yarn installed as a package manager
  • expo-cli (>=3.0.4), previously known as create-react-native-app
  • JavaScript/ES6 basics
Please note that, throughout this tutorial, we’ll be using an iOS simulator to demonstrate the application.

Table of Contents

  • Setting up a Crowdbotics app
  • Setting up the React Native app
  • Creating a simple WebView
  • Adding Navigation between two screens
  • Completing the WebView Screen
  • Linking to other apps
  • Handling deep links
  • Conclusion

Setting up a Crowdbotics app

In this section, you will be setting up a Crowdbotics project that has React Native and an Expo generated template with stable and current dependencies for you to leverage. At the time of writing this tutorial, the template is based on Expo SDK version 32. So instead of going into too much hassle about upgrading this React Native app, I will be walking you through creating a new React Native project in the next section.

To follow along, setting up a new project using Crowdbotics app builder service is easy. Visit the app.crowdbotics.com dashboard. Once you are logged in, choose Create New Application.

Crowdbotics dashboard with Create New Application icon

On the Create an Application page, choose React Native Expo template under Mobile App. Lastly, select the name of your app at the bottom of the page and then click the button Create my app!

After a few moments, your Crowdbotics project will be created. Upon its creation, it will redirect you to the app dashboard, where you can see a link to GitHub, Heroku, and Slack. Once your project is created, you will get an invitation from Crowdbotics to download your project or clone the repository from GitHub either via email or as a notification if you logged in using GitHub authentication.

Crowdbotics provides out-of-the-box solutions, including support and development of your application.

Setting up the React Native app

Once the app is generated and assuming you have either downloaded the project or cloned it in your local dev environment, navigate inside the directory. You will be welcomed by two sub-directories: backend and frontend. For this tutorial, you can ignore the backend directory and open the frontend directory in your favorite code editor or IDE.

The contents of an /src folder inside a filetree

One of the main reasons to use Expo for this tutorial is to have faster development setup. Crowdbotics' Expo app by default comes with a set of commonly used libraries in the React Native ecosystem. Some of these libraries are:

  • react-navigation which you are going to see in action later
  • native-base, one of the most popular UI component libraries
  • redux to manage the application's state

You can find the complete list of all the dependencies the project relies on inside the package.json file. Now, to install these dependencies, execute the below command.

yarn install

# or

npm install

After installing all the dependencies, run the command yarn run start and make sure either a simulator is running, or a real device is connected.

In this tutorial, you are not going to make use of Login or Signup screens that are provided by the Crowdbotics app. Let us make some changes in the navigation flow such that whenever the app reboots, it directly opens HomeScreen as the first screen.

Open the src/navigators/HomeNavigator.js file and modify it as follows:

import { createStackNavigator, createAppContainer } from 'react-navigation'

import Home from '../containers/Home'

const HomeNavigator = createStackNavigator({
  Home: {
    screen: Home,
    navigationOptions: {
      header: null
    }
  }
})

const HomeRootNavigator = createAppContainer(HomeNavigator)

export default HomeRootNavigator

In the above snippet, you are defining the HomeNavigator as the root level app container. This navigator is based on StackNavigation which is going to be useful when adding WebViews.

Import the newly created HomeRootNavigator inside the file App.js. Replace the existing AppNavigator with HomeRootNavigator as shown in the snippet below.

import React, { Component } from 'react'
import { StatusBar } from 'react-native'
import { Provider } from 'react-redux'

import HomeRootNavigator from './src/navigators/HomeNavigator'
import store from './src/redux/store'

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <StatusBar barStyle='light-content' />
        <HomeRootNavigator />
      </Provider>
    )
  }
}

export default App

Now, when the app runs, you will be welcomed by the following Home screen, which is the default screen in Crowdbotics Expo generated apps.

Default home screen displaying the text "Home"

Creating a simple WebView

To create a simple WebView component, all you have to do is import the WebView component from React Native core inside the file src/containers/Home/index.js file.

import React from 'react'
import { WebView } from 'react-native'

Then modify the functional Home component in the same file with the following WebView component.

const Home = () => <WebView source={{ uri: 'https://blog.crowdbotics.com' }} />

export default Home

The WebView component requires a source prop to load the static HTML in the form of a web page or a URI from a remote source where the web page already exists. You will get the following result.

Demo of scrolling through the Crowdbotics blog

Adding Navigation between two screens

In the previous demonstration, you can clearly observe that the WebView component is currently loading the Crowdbotics blog from the URI. However, it's missing some basic navigation mechanisms, such as how to exit the Web view mode when the user is done interacting with the web page and go back to the application.

To enhance the current flow, in this section, you are going to add another container screen in the stack navigator that is going to take care of displaying the web view.

Create a new sub-directory inside src/containers called WebViewScreen. Next, inside this directory, create a new file called index.js with the following code snippet. This component, for now, will act as a mock component for now.

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

class WebViewScreen extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>Webview Screen</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  },
  text: {
    color: '#333',
    textAlign: 'center'
  }
})

export default WebViewScreen

You should also modify the Home component. There is going to be a back button that will help navigate from the Home screen to the WebViewScreen when the navigation is added.

import React, { Component } from 'react'
import { Container, Content, Text, Button } from 'native-base'
import styles from './styles'

class Home extends Component {
  state = {
    title: 'Crowdbotics Blog',
    link: 'https://blog.crowdbotics.com'
  }

  handleButtonPress() {
    alert('You clicked')
  }

  render() {
    const { title } = this.state
    return (
      <Container style={styles.container}>
        <Content contentContainerStyle={styles.content}>
          <Button onPress={this.handleButtonPress} style={styles.button}>
            <Text style={styles.text}>{title}</Text>
          </Button>
        </Content>
      </Container>
    )
  }
}

export default Home

In the above snippet, notice that the initial state object holds two properties: title and link. The title is going to be displayed on the button as the text, and the link is going to be passed to the WebViewScreen to open the web page. Now run the application, and on the Home screen, there will be a button.

Home screen with "Crowdbotics Blog" button

Clicking the button won't do any good for now. So let us add the navigation between the two screens, such that you can pass the navigational props.

Go to navigators/HomeNavigator.js file and let us add the WebViewScreen component.

import { createStackNavigator, createAppContainer } from 'react-navigation'

import Home from '../containers/Home'
import WebViewScreen from '../containers/WebViewScreen'

const HomeNavigator = createStackNavigator({
  Home: {
    screen: Home,
    navigationOptions: {
      header: null
    }
  },
  WebViewScreen: {
    screen: WebViewScreen,
    navigationOptions: () => ({
      headerTintColor: '#fff',
      headerStyle: {
        backgroundColor: '#0a1142'
      }
    })
  }
})

const HomeRootNavigator = createAppContainer(HomeNavigator)

export default HomeRootNavigator

Stack Navigation provides a way to transit between screens. This mechanism works quite similar to how a web application works in a web browser. A web app either pushes (next page) or pops (go back) when navigating between different web pages in a browser. Similarly, different screens combined together are used to either push or pop in a React Native application using the stack navigation mechanism.

You will get the following result.

Demo of tapping "Crowdbotics Blog" button

Notice that Stack Navigator from react-navigation adds a default header if not specified. The WebViewScreen utilizes this header to navigate back to the Home screen by clicking the back button.

As the navigation between the two screens is working now, let's use navigation params to send the title from the Home component's state to the WebViewScreen. First, inside the HomeNavigator.js file, modify navigationOptions of WebViewScreen by passing a navigation argument and add a title property to it.

WebViewScreen: {
    screen: WebViewScreen,
    navigationOptions: ({ navigation }) => ({
      title: navigation.state.params.title,
      // rest remains same
    })
  }

Next, go to the Home/index.js file and pass the title from the initial state as an object that is the second parameter to the navigation props.

handleButtonPress = () => {
  const { title } = this.state
  this.props.navigation.navigate('WebViewScreen', { title })
}

The navigation.state.params allows reading the parameters passed through when handling the button press in the Home screen component. The props.navigation.navigate is used to figure out which screen or route to visit when the button is pressed on the home screen. The name of the route or the screen is passed as the first argument as you can notice in the above snippet.

Now go back to the simulator device, and you will notice that the header on WebViewScreen shows the title of the web page.

Demo of "Crowdbotics Blog" title rendering in browser

Completing the WebView Screen

Open the WebViewScreen.js component file and edit it with only one WebView component being returned inside the render method.

import React, { Component } from 'react'
import { WebView, ActivityIndicator } from 'react-native'

class WebViewScreen extends Component {
  LoadingIndicatorView() {
    return (
      <ActivityIndicator
        color='#0a1142'
        size='large'
        style={{
          flex: 1,
          justifyContent: 'center'
        }}
      />
    )
  }

  render() {
    const { params } = this.props.navigation.state
    return (
      <WebView
        source={{ uri: params.link }}
        renderLoading={this.LoadingIndicatorView}
        startInLoadingState={true}
      />
    )
  }
}

export default WebViewScreen

The WebView component from React Native uses props like startInLoadingState and renderLoading, which triggers the loading indicator—in our case, the function LoadingIndicatorView(). This loading indicator is a pragmatic approach when a web page takes time to load.

Also note that in the above snippet, you will find another way to pass data between two screens. The WebViewScreen is using navigation.state.params to get the link from the Home screen's initial state.

Will it work? The simple answer is no. Why? Because in the Home screen component, you are not passing the link  yet as the second argument for the WebViewScreen component to render its value.

Edit the following line inside Home/index.js component.

handleButtonPress = () => {
  const { title, link } = this.state
  this.props.navigation.navigate('WebViewScreen', { title, link })
}

Now, go back to the simulator and press the button. It opens a new screen that displays the Crowdbotics' blog.

Demo of opening the Crowdbotics blog in browser

You see, it does take time to load the complete web page, and ActivityIndicator component helps to display a spinner to convey the same message to the end-user of the application. This completes the WebView section.

Linking to other apps

The WebView component is great to render a third-party website within a React Native app. In the previous sections, you learned how to create and utilize the component from React Native's core.

As a mobile developer, there are going to be scenarios where you'll want to open the website or the link in the device's native browser or other native application. In these scenarios, you cannot use WebViews. Also, these scenarios are known as deep linking and are more common in mobile applications.

React Native's core API provides another module called Linking. This module allows interacting with other applications via deep links. It provides helper methods ready to use deep links in your current application. In the following sections, you are going to see it in action.

Open the Home/index.js file and modify the initial state object. The object now contains an array called links with three different items. Each item contains properties like title to display the text on the button, and link to designate the URI to follow when the button is clicked.

state = {
  links: [
    {
      title: 'Native Browser',
      link: 'https://blog.crowdbotics.com'
    },
    {
      title: 'Text Message',
      link: 'sms:+9910000000'
    },
    {
      title: 'Email',
      link: 'mailto:info@crowdbotics.com'
    },
    {
      title: 'YouTube',
      link:
        'https://www.youtube.com/watch?v=ypdk64ySgoo&list=PLSk21zn8fFZBKEJxmkdSzzmMJrxkfyjph'
    }
  ]
}

After modifying the initial state, let us move to the render function and display four buttons by mapping over the state.links array. To avoid the warning in a yellow box, also add a key prop to the Button component. This prop will use the index value since there is no unique identifier associated with any of the items in the links array.

render() {
    const { links } = this.state
    return (
      <Container style={styles.container}>
        <Content contentContainerStyle={styles.content}>
          {links.map((item, index) => (
            <Button
              onPress={() => this.handleButtonPress(item)}}
              style={styles.button}
              key={index}>
              <Text style={styles.text}>{item.title}</Text>
            </Button>
          ))}
        </Content>
      </Container>
    )
  }

Apart from traversing the array, the rest of the code remains the same. You will get the following result on a device.

App home page displaying native app links

From the previous section, you know that whenever the user presses the button for each link, it will use the helper method called handleButtonPress and pass the correct item from the state.links array. This argument item that is passed is then received by the handleButtonPress as shown below.

handleButtonPress = item => {
  Linking.openURL(item.link).catch(err =>
    console.error('An error occurred', err)
  )
}

Linking module has a method to open external links. These external links can vary in nature, such as we have defined in the state of the Home component. In the code snippet above, the method openURL requires a valid URL to be passed for it to work. This method is promise-based, so using a catch clause to catch errors or to display them is a good practice.

Let us test these out. Click the first button that says Native Browser and it should open the link in the device's browser.

Demo of clicking "Native Browser" link

The default web browser opens, and it loads the website. There is no WebView being used, so you do not have to add navigation strategy to handle this. Now, click on the button Text Message and notice it should open the default SMS or text service application.

Demo of clicking "Text message" link

The third object in the links array is to open the default Email client app. Since there is no email client app install on iOS simulator, I am going to show you the demo of running the application on a real Android device.

Demo of clicking "Email" link

It works like a charm by opening the Gmail client app on the real device. Now, lastly, let us find out what happens when you click on the button that says YouTube. It will work in a similar manner and open the video link in the YouTube application.

Demo of clicking "YouTube" link

Conclusion

By now, you understand how to create a WebView component or use the Linking module in your React Native application. Deep linking is important in mobile app development, and this tutorial provides you with an introduction on how to get started and not to get stuck in the beginning.

I hope this tutorial provided you an easy walkthrough to grab the basic concepts and build something of your own.

If you like this post, please leave a shoutout for me. I am always available on Twitter or you can check the rest of my tutorials here.

You will find the complete source code at this Github repository.