Using WebViews to Connect a React Native App to Web

As a React Native developer, you'll come across use cases that will require you to embed or redirect a web application or a web page inside a React Native app. WebViews are often used for such use cases.

A community-maintained React Native module, WebViews are the only way to let the user visit external links within an iOS or Android application. The WebView component in React Native core first became available in React Native version 0.57.x.

In this tutorial, you are going to learn how to utilize a primary WebView component using the react-native-webview npm module, which is the official cross-platform module to attain the functionality.

Table of Contents

  • Requirements
  • Getting started with Crowdbotics App Building Platform
  • Installing the WebView module
  • Implementing a Simple WebView
  • Adding an Activity Indicator
  • Handling navigation when using Webviews
  • Conclusion

Requirements

  • Nodejs version <= 10.x.x installed
  • watchman installed
  • Have access to one package manager such as npm or yarn
  • use react native version 0.60.x or above

Getting started with the Crowdbotics App Builder


To generate a new React Native project, you can use the react-native cli tool. Or, if you want to follow along, I am going to generate a new app using the Crowdbotics app building platform.

Crowdbotics Overview page

Register either using your GitHub credentials or your email. Once logged in, you can click the Create App button to create a new app. The next screen is going to prompt you as to what type of application you want to build. Choose Mobile App.

Crowdbotics app type selection screen

Enter the name of the application and click the button Create App. After you link your GitHub account from the dashboard, you are going to have access to the GitHub repository for the app. This repo generated uses the latest react-native version and comes with built-in components and complete examples that can be the base foundation for your next app.

That's it. It's a three-step process. Now, let's get back to our tutorial.

Installing WebView module

To use a WebView component, you have to install the react-native-webview package by executing the below command from a terminal window.

yarn install react-native-webview

If you are using a react-native version above 0.60.x for iOS devices, make sure you install pods by navigating inside the ios/ directory and execute the command pod install.

For Android users, if you are using the latest react-native-webview version (which you are), open the file android/gradle.properties and make sure the following two lines exist. If not, add them.

android.useAndroidX=true
android.enableJetifier=true

Once the dependency installation is done, let's run the application. We are going to use an iOS simulator for this tutorial. If you are on Windows- or Linux-based operating systems, you can use Android Studio.

Run the command as stated below to open the boilerplate application that comes with react-native-cli.

# for Mac users
react-native run-ios

# for Windows/Linux users
react-native run-android

If the app opens without any error, that means the configuration we have done so far is good to go.

Implementing a Simple WebView

In this section, let's create a simple WebView component and understand how it works. Start by importing the WebView component from react-native-webview to render web content in a native view. Open the App.js file.

import React from 'react'
import WebView from 'react-native-webview'

The WebView component requires a source prop. This prop loads the static HTML or a URI (which is the current case if you look closely at the above snippet). A URI is a remote location for a web page to exist.

The style prop is basic React Native inline styling that allows you to customize a WebView. Inside the App function component, let's render this simple WebView component.

const App = () => {
  return (
    <WebView
      source={{ uri: 'https://blog.crowdbotics.com/' }}
      style={{ marginTop: 40 }}
    />
  )
}

export default App

To view this in action, make sure you build the React Native app for the first time using either of the commands specified below from a terminal window. Do note that these commands are platform-specific. For Android users, if you are using a real device or a simulator, make sure it is running first.

# for iOS
react-native run-ios

# for android
react-native run-android

After that, you are going to see a similar output as below:

The app loads the Crowdbotics blog

Adding an Activity Indicator

Did you notice that when the screen or the component loads for the first time, it just shows a blank white screen for a few seconds? This indicates that the web page is loading from the remote source. However, in a real-time application, you have to provide some type of loading indicator to the user to imply that the web page is being currently loaded.

This can be done by adding an ActivityIndicator component from the react-native core. It is going to display a spinner on the device's screen when the web page is in the loading state.

In the App.js file, import the following statement after the other statements.

// ... rest of the statements
import { ActivityIndicator } from 'react-native'

To display the indicator when the web page is in a loading state, WebView requires you to add two props. The first prop startInLoadingState prop must be set to true. The second prop renderLoading is responsible for triggering the activity indicator. It always accepts a function as its value. The value of the function is going to be the ActivityIndicator component.

const App = () => {
  return (
    <WebView
      source={{ uri: 'https://blog.crowdbotics.com/' }}
      style={{ marginTop: 40 }}
      startInLoadingState={true}
      renderLoading={() => (
        <ActivityIndicator
          color='blue'
          size='large'
          style={{
            flex: 1
          }}
        />
      )}
    />
  )
}

Both of these props are not available by default. You have to specify them explicitly. Take a look at how it works on the below screen.

A loading spinner appears when the web page is loading

This completes how to implement a simple WebView component in any React Native app. In the next section, let's tackle one of the problems this simple component lacks: it does not have a navigation button to navigate back or forward as the state of the URL changes (just like in a web browser).

Handling navigation when using WebViews

The WebView API is vast in React Native and provides most common functionalities that you need to support different features in the app.

The WebView API provides some methods like goBack and goForward to handle navigation state and transitions. For example, the goBack method allows the user to go back one page in the web view's history. Vice-versa is true for the method goForward.

This navigation between web pages has to be done only when there is a way to store or listen to the URL change. Fortunately for us, there is a prop called onNavigationStateChange that represents the navigation state of the component. You just need to pass the current URL and keep track of the previous and forward buttons.

The current URL can be passed by creating a ref object which is the approach you are going to use in this demo app. It holds a mutable .current property that can be used to uniquely identify the URL.

Enough with theory – let's see the pragmatic use. I am going to use the latest Hooks syntax. If you are using the counterpart of the functional components, please make sure to check how to use ref property on the WebView instance inside the class component.

For those who have been following this tutorial so far, please make sure that you import hooks such as useRef, and useState from React. Also, import some more components from the react-native core that are going to help us add a footer to the app screen. This footer is going to have two buttons: one to go to the previous URL and one to go to the forward URL (if exists).

import React, { useRef, useState } from 'react'
import { ActivityIndicator, TouchableOpacity, View, Text } from 'react-native'
import WebView from 'react-native-webview'

Inside the functional component App, let's create three state variables for the following purposes:

  • canGoBack: to go the previous web page from the navigational state. Its initial value is going to be a boolean false.
  • canGoForward: to go to the next web page in the navigational state. Its initial value is going to be a boolean false.
  • currentUrl to keep a reference of the current URL. Its initial value is going to be an empty string.
const App = () => {
  const [canGoBack, setCanGoBack] = useState(false)
  const [canGoForward, setCanGoForward] = useState(false)
  const [currentUrl, setCurrentUrl] = useState('')

  //...
}

Next, using useRef creates a webviewRef.

const webviewRef = useRef(null)

Then create two handler methods that are going to handle the navigational state transition in real-time using the mutable property current on a button press.

backButtonHandler = () => {
  if (webviewRef.current) webviewRef.current.goBack()
}

frontButtonHandler = () => {
  if (webviewRef.current) webviewRef.current.goForward()
}

Add the props ref and onNavigationStateChange to the WebView component and wrap it inside a View.

The navState is going to track the state changes as well as fetch and set the current URL as shown below in the code snippet.

<View style={{ flex: 1 }}>
  <WebView
    source={{ uri: 'https://blog.crowdbotics.com/' }}
    style={{ marginTop: 40 }}
    renderLoading={() => (
      <ActivityIndicator
        color='#2ed2d2'
        size='large'
        style={{
          flex: 1
        }}
      />
    )}
    startInLoadingState={true}
    ref={webviewRef}
    onNavigationStateChange={navState => {
      setCanGoBack(navState.canGoBack)
      setCanGoForward(navState.canGoForward)
      setCurrentUrl(navState.url)
    }}
  />
</View>

After the WebView component, create another View component that holds two buttons. Each of the buttons is defined from TouchableOpacity that has an onPress prop. This prop is going to make use of the handler methods you defined earlier.

<View
  style={{
    padding: 20,
    flexDirection: 'row',
    justifyContent: 'space-around',
    backgroundColor: '#2ed2d2'
  }}>
  <TouchableOpacity onPress={backButtonHandler}>
    <Text style={{ color: 'white', fontSize: 24 }}>Back</Text>
  </TouchableOpacity>
  <TouchableOpacity onPress={frontButtonHandler}>
    <Text style={{ color: 'white', fontSize: 24 }}>Forward</Text>
  </TouchableOpacity>
</View>

To see it in action, go back to the simulator/device of your choice. The first thing you are going to notice is the bottom footer on the screen.

The app now includes a footer with Back and Forward buttons

Here is the complete demo in action with back and forward buttons working.

The Back and Forward buttons are functional

Conclusion

WebViews might not be the prominent way to create mobile apps, but they can add important functionality to handle use cases where there is a requirement to connect web interfaces and native code. The WebView component has a great API that you can refer to here.

Originally published:

April 30, 2020