WebView — Navigation and data flow with WebViews in a React Native app
Tags: reactnative, webview, crossplatform, react
Published: 2020-05-19 09:31:09

WebViews in React Native are the only way to let the user visit external links within an iOS or Android application. WebView can be used for embedding or running a web application inside our React Native app that’s good especially if you have a web app and you want to connect that app with your React Native app.

## What we’re going to build

- A WebView and custom navigation to handle navigation in WebView.
- How to Pass data to React Native from WebView.
- How to Pass data to WebView from React Native.

## Prerequisites

- Node.js version <= 10.x.x installed
- Have access to one package manager such as npm or yarn
- Use React Native version 0.60.x or above

Let start by creating a new React Native project for the example application I'm using [react-native-cli](https://github.com/react-native-community/cli)

```javascript
react-native init RNWebViewExample
```

This will bootstrap the project and all the necessary files to start working with React native. 🎉

To run react-native app

```javascript
# for ios
react-native run-ios
# for android
react-native run-android
```

## Installing the dependencies

We’re relying on the following packages to implement the functionalities needed by the app:

- `react-native-webview` - the package allow us to use WebView within a React Native app.
- `@react-navigation/native` - the package allow us to use the navigation in React Native app.
- `@react-navigation/stack` - the package allows us to create stack navigator in react-navigation.
- `react-native-gesture-handler` - the package provides native-driven gesture management.

We are using react-native v.62 so there’s no additional linking needed for the above packages. I hope you’ve successfully installed it. If you’re stuck somewhere, please refer to the official installation guide for above packages. We’re going to use an iOS simulator for this tutorial. If you’re on Windows or Linux based operating systems, you can use Android Studio.

## Adding Screens and Navigation

The `App` Component is going to be responsible for initializing screens and create Stack Navigator. Add the import statements that are going to help construct a stack navigator. We are using the createStackNavigator function to create a stack-based navigation flow. This function takes a route configuration object and an options object and returns a React component. Right now, it has only one screen that will be displaying some buttons to navigate to a specific WebView.
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 the browser. Similarly, different screens can be used to either push or pop between in a React Native application.

```javascript
import React from "react";
import { StatusBar } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import Home from "./src/screens/RootScreen";
import WebToNative from "./src/screens/WebToNative";
import NativeToWeb from "./src/screens/NativeToWeb";
import WebViewUI from "./src/screens/WebView";

const Stack = createStackNavigator();

function App() {
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <NavigationContainer>
        <Stack.Navigator initialRouteName="Home">
          <Stack.Screen
            name="Home"
            component={Home}
            options={{
              headerTintColor: "green",
              title: "Home",
            }}
          />

          <Stack.Screen
            name="WebViewUI"
            component={WebViewUI}
            options={{
              headerTintColor: "green",
              title: "WebViewUI",
            }}
          />
          <Stack.Screen
            name="WebToNative"
            component={WebToNative}
            options={{
              headerTintColor: "green",
              title: "WebToNative",
            }}
          />
          <Stack.Screen
            name="NativeToWeb"
            component={NativeToWeb}
            options={{
              headerTintColor: "green",
              title: "NativeToWeb",
            }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </>
  );
}
export default App;
```

If you’re new to the React Navigation library, be sure to check their getting started guide [react-navigation](https://reactnavigation.org/docs/getting-started/).

# WebView and custom navigation to handle navigation in WebView

The WebView component requires two props to make it work. First, the startInLoadingState prop must be set to true, as shown in the below snippet. Then, the renderLoading prop is responsible for triggering the loading indicator, in our case, the function: LoadingIndicatorView(). We are going to use the webpage by its URL. But if you want to load HTML directly, you can use the HTML property in WebView’s source property, HTML as a source I'm going to explain later

```javascript
<WebView
  source={{ uri: "https://dev.to/" }}
  renderLoading={LoadingIndicatorView}
  startInLoadingState={true}
  ref={webviewRef}
/>
```

### Handing navigation using WebView

Inside the `WebViewUI` component, let’s create three event handler for navigation 

- `webViewgoback`: to go the previous web page inside the WebView

- `webViewNext`: to go to the next web page in the WebView.

- `props.navigation.navigate("Home")`: to go home component in react-native.

```javascript
import React from "react";
import {
  View,
  Text,
  ActivityIndicator,
  TouchableOpacity,
  SafeAreaView,
  StyleSheet,
} from "react-native";
import { WebView } from "react-native-webview";

function WebViewUI(props) {
  const webviewRef = React.useRef(null);

  function webViewgoback() {
    if (webviewRef.current) webviewRef.current.goBack();
  }

  function webViewNext() {
    if (webviewRef.current) webviewRef.current.goForward();
  }

  function LoadingIndicatorView() {
    return (
      <ActivityIndicator
        color="#009b88"
        size="large"
        style={styles.ActivityIndicatorStyle}
      />
    );
  }
  return (
    <>
      <SafeAreaView style={styles.flexContainer}>
        <WebView
          source={{ uri: "https://dev.to/" }}
          renderLoading={LoadingIndicatorView}
          startInLoadingState={true}
          ref={webviewRef}
        />
        <View style={styles.tabBarContainer}>
          <TouchableOpacity onPress={webViewgoback}>
            <Text style={{ color: "green" }}>Back</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => props.navigation.navigate("Home")}>
            <Text style={{ color: "green" }}>Exit</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={webViewNext}>
            <Text style={{ color: "green" }}>Next</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    </>
  );
}

const styles = StyleSheet.create({
  ActivityIndicatorStyle: {
    flex: 1,
    justifyContent: "center",
  },
  flexContainer: {
    flex: 1,
  },
  tabBarContainer: {
    backgroundColor: "#d3d3d3",
    height: 56,
    alignItems: "center",
    flexDirection: "row",
    paddingHorizontal: 16,
    justifyContent: "space-between",
  },
  button: {
    fontSize: 24,
  },
  arrow: {
    color: "#ef4771",
  },
  icon: {
    width: 20,
    height: 20,
  },
});
export default WebViewUI;
```

Here’s the complete demo in action with the Back, Next and Exit buttons working.

![alt text](https://res.cloudinary.com/dfsmn69z2/image/upload/v1589737503/1_ihinxw.gif "Logo Title Text 1")

## How to Pass data to React Native from WebView

Above section, we saw how to load a webpage by its URL. But if you want to load HTML directly, you can use the HTML property in WebView’s source property, as shown below. If the webpage wants to send/communicate something back to your React Native code? That is where we can use `window.ReactNativeWebView.postMessage` and the `onMessage` prop as listener in react-native.

`window.ReactNativeWebView.postMessage(JSON.stringify(data))` sends data to react native

**NOTE**: window.ReactNativeWebView.postMessage only accepts one argument, which must be a string.

```javascript
import React from "react";
import { ActivityIndicator, SafeAreaView, StyleSheet } from "react-native";
import { WebView } from "react-native-webview";

function WebToNative(props) {
  const webviewRef = React.useRef(null);
  function onMessage(data) {
    alert(data.nativeEvent.data);
    console.log(data.nativeEvent.data);
    props.navigation.navigate("Home");
  }

  function LoadingIndicatorView() {
    return (
      <ActivityIndicator
        color="#009b88"
        size="large"
        style={styles.ActivityIndicatorStyle}
      />
    );
  }
  return (
    <>
      <SafeAreaView style={styles.flexContainer}>
        <WebView
          source={{
            html: `<body style="display:flex; justify-content:center;flex-direction:column;align-items:center">
                     <h2>React native webview</h2>
                     <h2>React native webview data transfer between webview to native</h2>
                     <button style="color:green; height:100;width:300;font-size:30px"
                      onclick="myFunction()">Send data to Native</button>
                     <p id="demo"></p>
                     <script>
                       const data = [
                           'Javascript',
                           'React',
                           'React Native',
                           'graphql',
                           'Typescript',
                           'Webpack',
                           'Node js',
                        ];
                      function myFunction() {
                        window.ReactNativeWebView.postMessage(JSON.stringify(data))
                      }
                      var i, len, text;
                      for (i = 0, len = data.length, text = ""; i < len; i++) {
                      text += data[i] + "<br>";
                      }
                     document.getElementById("demo").innerHTML = text;
                    </script>
                 </body>`,
          }}
          renderLoading={LoadingIndicatorView}
          startInLoadingState={true}
          ref={webviewRef}
          onMessage={onMessage}
        />
      </SafeAreaView>
    </>
  );
}

const styles = StyleSheet.create({
  ActivityIndicatorStyle: {
    flex: 1,
    justifyContent: "center",
  },
  flexContainer: {
    flex: 1,
  },
});
export default WebToNative;
```

You will get the following result in the simulator running the application.

![alt text](https://res.cloudinary.com/dfsmn69z2/image/upload/v1589737445/3_d8mioj.gif "Logo Title Text 1")

# How to Pass data to WebView from React Native

In the above section, we leaned in details how to Pass data to WebView from React Native. Now we are going to learn how to pass data to WebView from React Native. To pass data to WebView we are going to use `injectedJavaScript` method in the WebView component.

```javascript
document.addEventListener("message", function(event) {
 alert(event.data)
}
```

Using the above message EventListener we can get the data from react-native to WebView.

```javascript
import React from "react";
import { ActivityIndicator, SafeAreaView, StyleSheet } from "react-native";
import { WebView } from "react-native-webview";

function NativeToWeb(props) {
  const webviewRef = React.useRef(null);
  const data = [
    "Javascript",
    "React",
    "React Native",
    "graphql",
    "Typescript",
    "Webpack",
    "Node js",
  ];
  const runFirst = `
      document.body.style.backgroundColor = 'green';
      setTimeout(function() { window.alert(JSON.stringify([
             'Javascript',
             'React',
             'React Naitve',
             'graphql',
             'Typescript',
             'Webpack',
             'Node js',
          ])) }, 1000);
      true; // note: this is required, or you'll sometimes get silent failures
    `;

  function onMessage(data) {
    props.navigation.navigate("Home");
  }

  function LoadingIndicatorView() {
    return (
      <ActivityIndicator
        color="#009b88"
        size="large"
        style={styles.ActivityIndicatorStyle}
      />
    );
  }
  return (
    <>
      <SafeAreaView style={styles.flexContainer}>
        <WebView
          source={{
            html: `<body style="display:flex;justify-content:center;flex-direction:column;align-items:center">
                      <h2>React native webview</h2>
                      <h2>React native webview data transfer between Native to web</h2>
                      <button style="color:green; height:100;width:300;font-size:30px"
                        onclick="myFunction()">Close webview</button>
                      <p id="demo"></p>
                      <script>
                       var newData = [];
                       document.addEventListener("message", function(data) {
                       newData.push(data.data)
                       alert(data.data)
                       var i, len, text;
                       for (i = 0, len = newData.length, text = ""; i < len; i++) {
                       text += newData[i] + "<br>";
                       }
                       document.getElementById("demo").innerHTML = text;
                      });
                      function myFunction() {
                      window.ReactNativeWebView.postMessage('Hello')
                      }
                    </script>
           </body>`,
          }}
          renderLoading={LoadingIndicatorView}
          startInLoadingState={true}
          ref={webviewRef}
          onMessage={onMessage}
          injectedJavaScript={runFirst}
        />
      </SafeAreaView>
    </>
  );
}

const styles = StyleSheet.create({
  ActivityIndicatorStyle: {
    flex: 1,
    justifyContent: "center",
  },
  flexContainer: {
    flex: 1,
  },
});
export default NativeToWeb;
```

You will get the following result in the simulator running the application.

![alt text](https://res.cloudinary.com/dfsmn69z2/image/upload/v1589737448/2_kg9ddx.gif "Logo Title Text 1")

# Conclusion

This article concludes how to get started, setting up, and using a WebView component in a React Native application. the important part of this article how to handle the navigation inside the WebView and pass the data from react-native to webview and webview to react-native

You can find the complete code for this article at here
{% github https://github.com/mukeshmandiwal/RNWebViewExample %}