Categories
How To React Native

React Native WebView: How To Get the Web on Your App

In this guide I will show you how React Native WebView lets you:

Display local html and webpages in your application.

Track and control page navigation.

Inject and post/send javascript.

Let’s dive right in.

react native webview

In this guide I will show you how React Native WebView lets you:

Display local html and webpages in your application.

Track and control page navigation.

Inject and post/send javascript.

Let’s dive right in.

React native webview: getting started

CHAPTER 1: Getting Started With React Native WebView

To render the web on your app, there is really only one go to library:

The react-native-webview package developed by the React Native Community.

Here is what you should know about the library and how to get started.

The Essentials

Before we dive into the code, let’s shortly cover the essentials:

One simple component: React Native WebView is an actively maintained React Native component, which needs to be installed as an independent package inside your project.

Project type support: WebView is available for both bare React Native apps as well as Expo build applications. The latter has added support in SDK version 33.0, which was released in the middle of 2019.

Device type support: WebView offers support for all platforms, including iOS, Android, iOS Simulator and Android Simulator.

The project’s core maintainers are:

Now that we have these out of the way, let’s get started.

Getting Started

The WebView Getting Started Guide is documented in detail on the official React Native WebView Github page.

But in case this is your starting point to WebView, these are the quick steps:

Step 1) Create a new React Native Project

The easiest and quickest way to start a React Native project is with the Expo command line tools. This way we avoid having to install native dependencies, which can be rather troublesome when doing so for the first time.

Note: It is possible to ‘eject’ from Expo projects at any moment to take back control over the native code compilation process.

…so, to get started, run:

$ npm install --global expo-cli

# Create a new project
$ expo init YOURPROJECTNAME

$ cd YOURPROJECTNAME
$ expo start

Debug Tip 1: If your project fails to load on your iOS simulator or Android Emulator, make sure to restart your simulator / emulator software and try again.

Debug Tip 2: If the Expo Cli application tells you your project is installed with a later version of Expo, simply uninstall the Expo Cli app from your simulator device and run: npm run ios

Step 2) Add react-native-webview Dependencies

Next, run the following command:

$ expo install react-native-webview

As we’re running our project with Expo support, Expo is taking care of linking native dependencies. This means our project setup is now complete!

React native webview: loading html and web page urls

CHAPTER 2: Loading HTML and Web Page URLs

Now that our project is installed, it’s time to get our first WebView up and running.

The good news:

We can get our first View up in just a couple lines of code.

Here is what we need.

Displaying a Web Page

Inside your App.js file, update the App function as follows:

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

export default function App() {
  return (
    <WebView
        style={{ marginTop: 35 }}
        source={{ uri: 'https://knowlephant.com' }}
    />
  );
}

Any web page can be rendered this way.

Use case: Load in-app pages that are hosted on your personal website. For instance, a WordPress blog that makes your blog articles available to your readers on an app.

Displaying HTML

Being able to display web pages in React Native applications is nice. But sometimes, all we need to show is a small section of HTML. Here is how.

Again, in App.js, replace your code with the following:

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

export default function App() {
  return (
    <WebView
      style={{
        marginTop: 35,
      }}
      originWhitelist={['*']}
      source={{
        html: `<h1>Hello World</h1>`,
      }}
    />
  );
}

There are two important things to note in this example:

1) The use of originWhitelist props

The originWhitelist prop lets you to enter an array of strings with web address origins

What this does is it lets you indicate which pages the WebView is allowed to navigate to. Then, if a user presses a link that has a web address who’s origin is not included in the originWhitelist array, the navigation will be handled by the OS instead (taking your app to the background as a result).

Note: Default whitelisted origins are: ‘http://’ and ‘https://’.

2) Problems With Tiny Text on iOS

While everything appears to be looking fine on Android devices, iOS renders the html very tiny. 

This is solved by rendering the html inside the viewport meta tag as follows:

export default function App() {
const HTML = '<h1>Hello World</h1><span>A link to <a href="https://reactnative.dev/">React Native</a><span>';
const VIEWPORT_ADJUSTED_HTML = `<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body>${HTML}</body></html>`

  return (
    <WebView
      style={{
        marginTop: 35,
      }}
      originWhitelist={['*']}
      source={{
        html: VIEWPORT_ADJUSTED_HTML,
      }}
    />
  );
}

This solution is great for displaying html that is loaded from an external API.

Use case: Create a custom app screen with a list of blog posts which renders titles and descriptions that are loaded from your WordPress blog’s REST API.

React native webview: navigation

CHAPTER 3: Navigating Between Different Web Pages

The use case for in-app web views would not be so great if it would not be possible to navigate back and forward between different web pages.

This means that adding navigation buttons is essential when implementing the React Native WebView for your html and web pages.

Let’s see how this is done.

Registering Changes With onNavigationStateChange 

Adding navigation control to our WebView can be done quick and easy. In this example, this is what we would like to achieve:

React native webview: navigating between web pages
The FooterView component contains two navigation buttons that will appear and disappear based on the current navigation state for each action

The following code implements a ContentView component that includes a WebView with a custom FooterView component containing the ‘Forward’ and ‘Back’ buttons. 

import React, { useRef, useState } from 'react';
import { View, Platform } from 'react-native';
import { WebView } from 'react-native-webview';

import FooterView from './FooterView';

import { BLOG_HEADER_MARGIN_TOP } from '../config/constants';

import { contentViewStyles as styles } from './styles';

const ContentView = ({ data }) => {
  const webViewRef = useRef();

  const [canGoBack, setCanGoBack] = useState(false);
  const [canGoForward, setCanGoForward] = useState(false);

  const handleBackPress = () => {
    webViewRef.current.goBack();
  };

  const handleForwardPress = () => {
    webViewRef.current.goForward();
  };

  return (
    <View style={styles.container}>
      <WebView
        ref={webViewRef}
        containerStyle={[
          styles.webview,
          Platform.OS === 'android' && styles.$margin,
        ]}
        source={{
          uri: data.link,
        }}
        onNavigationStateChange={(navState) => {
          setCanGoBack(navState.canGoBack);
          setCanGoForward(navState.canGoForward);
        }}
        contentInset={{ top: BLOG_HEADER_MARGIN_TOP }}
        bounces={false}
      />
      {(canGoBack || canGoForward) && (
        <FooterView
          canGoBack={canGoBack}
          canGoForward={canGoForward}
          onBackPress={handleBackPress}
          onForwardPress={handleForwardPress}
        />
      )}
    </View>
  );
};

export default ContentView;

WebView provides navigation methods – goForward and goBack – that can be called by setting a reference to WebView.

Note: This way of navigating between web pages will not work for hash URL changes, such as from https://example.com/users#list to https://example.com/users#help

Use case: Another implementation of onNavigationStateChange could be to set a custom navigation button inside the app’s navigation bar and control back navigation to direct to either the previous website page or the previous app screen based on the WebView navigation state. 

React native webview: injecting webview with javascript

CHAPTER 4: Injecting React Native WebView With JavaScript

Display HTML or a web page in your app? Check.

Navigating between pages? Check.

Now it’s time to make our HTML or web page interactive.

How? By injecting JavaScript into our WebView.

Displaying Alerts

The most simple use case for injecting JavaScript in our WebView is by showing an alert based on user’s interaction with a web page.

For this, we would need to:

  1. Register a link press inside the web page.
  2. Check the link url value.
  3. Display the alert based on a condition.
React native webview: displaying alerts

The code example below uses the ContentView component from before, but this time applying the injectJavaScript method:

export default ContentView = () => {
  const webViewRef = useRef();

  const [currentUrl, setCurrentUrl] = useState(null);

  useEffect(() => {
    const showAlert =
      'window.alert("You are about to navigate to another blog article")';
    if (currentUrl === 'https://www.knowlephant.com/category/uncategorized/') {
      webViewRef.current.injectJavaScript(showAlert);
    }
  });

  return (
    <View style={{ flex: 1 }}>
      <WebView
        ref={webViewRef}
        originWhitelist={['*']}
        source={{
          uri: 'https://knowlephant.com/',
        }}
        onNavigationStateChange={(navState) => {
          setCurrentUrl(navState.url);
        }}
      />
    </View>
  );
};

There’s just one thing to note in this example, which is that the injected javascript parameter must be of type string

Use Case: Provide users with a warning message when visiting particular URL’s, indicating that the mobile experience might differ from desktop.

Integrating WebView JavaScript Injection With Native Components

Let’s take the above example one step further.

Consider the following scenario:

  1. The user presses a link.
  2. A React Native component is rendered on top of the WebView asking for user input.
  3. Based on the input, we control page navigation through JavaScript injection.
React native webview: integrating javascript with native

Here is the code implementation for this scenario:

export default ContentView = () => {
  const webViewRef = useRef();

  const [currentUrl, setCurrentUrl] = useState('');
  const [showModal, setShowModal] = useState(false);

  const alertOptions = {
    title: 'I Am A React Modal',
    body:
      'Sorry. This page is "members only". We will redirect you back to our homepage.',
    buttonStyle: 'horizontal',
    buttons: [
      {
        title: 'OK',
        onPress: () => {
          const newURL = '/';
          const redirectTo = 'window.location = "' + newURL + '"';

          setShowModal(false);
          webViewRef.current.injectJavaScript(redirectTo);
        },
      },
    ],
  };

  useEffect(() => {
    if (currentUrl === 'https://www.knowlephant.com/category/uncategorized/') {
      setShowModal(true);
    }
  });

  return (
    <View style={{ flex: 1 }}>
      <CustomModal
        showModal={showModal}
        onModalDismissPress={() => setShowModal(false)}
        options={alertOptions}
      />
      <WebView
        ref={webViewRef}
        originWhitelist={['*']}
        source={{
          uri: 'https://knowlephant.com/',
        }}
        onNavigationStateChange={(navState) => {
          if (
            navState.url ===
            'https://www.knowlephant.com/category/uncategorized/'
          ) {
            webViewRef.current.stopLoading();
            setShowModal(true);
          }
        }}
      />
    </View>
  );
};

There are a few things to note:

1) A CustomModal is rendered based on the showModal state.

2) Once a link is pressed, and it meets the specified condition, stopLoading is called — which is a WebView method — and the modal is shown.

3) A window.redirect is set to the redirect url once the React Native button is pressed.

4) The example uses the injectJavaScript method. The same effect could also be achieved by making use of the injectedJavaScript prop on the WebView component.

5) If you need to inject JavaScript before the content is loaded, use the injectedJavaScriptBeforeContentLoaded prop.

Many more similar scenario’s could be imagined.

Use Case: Navigation to Members Only sections could be intercepted, sending users to in-app payment or registration screens. Once registered, the redirection to the Members Section could be completed. 

React native webview: sending javascript from webview

CHAPTER 5: Sending JavaScript From React Native WebView

Injecting JavaScript into WebView is a great feature, but what if we have a use case in which we want to process JavaScript sent from WebView?

Communicating between JS and Native is possible in both directions.

Here’s how.

Applying the postMessage Method and onMessage Prop

This example assumes that we want to render a custom native component based on JavaScript output sent from our WebView component.

Consider the following example:

  1. A WebView that renders an HTML chart.
  2. User presses chart elements.
  3. JS is parsed to Native which renders a View with a Text component showing the pressed value.
React native webview: how to send javascript to native

…and here’s the code example:

export default ContentView = () => {
  const [value, setValue] = useState(null);

  const HTML = `
  <!DOCTYPE html>
      <head>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      </head>
      <script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
      <body>
        <canvas id="myChart"></canvas>
        <script>
        var data = {
            datasets: [{
              data: [300, 50, 100],
              backgroundColor: [
                "#F7464A",
                "#46BFBD",
                "#FDB45C"
              ]
            }],
            labels: [
              "Red",
              "Green",
              "Yellow"
            ]
          };
          
          
              var canvas = document.getElementById("myChart");
              var ctx = canvas.getContext("2d");
              var myNewChart = new Chart(ctx, {
                type: 'pie',
                data: data
              });
          
              canvas.onclick = function(evt) {
                var activePoints = myNewChart.getElementsAtEvent(evt);
                if (activePoints[0]) {
                  var chartData = activePoints[0]['_chart'].config.data;
                  var idx = activePoints[0]['_index'];
          
                  var label = chartData.labels[idx];
                  var value = chartData.datasets[0].data[idx];
          
                  window.ReactNativeWebView.postMessage("The selected value is: " + value)
                }
              };
      </script>
      </body>
      </html>
    `;

  return (
    <View style={{ flex: 1 }}>
      <WebView
        source={{
          html: HTML,
        }}
        onMessage={(event) => {
          setValue(event.nativeEvent.data);
        }}
      />
      <View style={styles.displayView}>
        <Text style={styles.text}>{value}</Text>
      </View>
    </View>
  );
};

One thing to note: the onMessage method will only accept a parameter of type string.

Use Case: Chart.js is a great way to render charts elements in your app without the need to install a React Native chart library in your application. Sending JS by interacting with Chart.js makes it possible to render chart date inside native views on top of WebView.

React native webview: how to show a loading view

BONUS CHAPTER: How To Render a Loader in React Native WebView

All the above examples have one thing in common:

Each time we render our WebView content…

…we are greeted with a white screen for a short period each time while the page was loading.

Let’s do something about this.

Rendering an Activity Indicator

I will keep this short and simple.

The goal is to render a loader – or ActivityIndicator component in React Native – in green, which will show when a web page or html is loading in WebView.

React native webview: showing an loader in webview
For more about React Native AcitivityIndicator: check out the post

For this, we need to apply the following two props:

  • startInLoadingState (must be true)
  • renderLoading (returns the loading View)

This is what the implementation will look like:

export default ContentView = () => {

  const renderLoadingView = () => (
    <View style={styles.container}>
      <ActivityIndicator size="large" color={styles.$primary} />
    </View>
  );

  return (
    <View style={{ flex: 1 }}>
      <WebView
        source={{
          html: HTML,
        }}
        onMessage={(event) => {
          setValue(event.nativeEvent.data);
        }}
        startInLoadingState
        renderLoading={renderLoadingView}
      />
  );
};

It’s as simple as that!

Note I: ActivityIndicator can be directly imported from react-native

Note II: renderLoading can only be used when startInLoadingState is set to true.

react native webview do it yourself

Share Your Idea

In this guide I have showed a number of examples with the purpose of helping you make the best use of the powerful React Native WebView library.

Now it’s time to turn it over to you:

Are you going to implement WebView in your next project?

Which functions do you plan to apply?

Or perhaps you’d like to ask a question about anything in this post.

Either way, I look forward to find out by reading your comment below.

Leave a Reply