DEV Community

Mukesh Mandiwal
Mukesh Mandiwal

Posted on

WebView — Navigation and data flow with WebViews in a React Native app

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

react-native init RNWebViewExample
Enter fullscreen mode Exit fullscreen mode

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

To run react-native app

# for ios
react-native run-ios
# for android
react-native run-android
Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

If you’re new to the React Navigation library, be sure to check their getting started guide react-navigation.

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

<WebView
  source={{ uri: "https://dev.to/" }}
  renderLoading={LoadingIndicatorView}
  startInLoadingState={true}
  ref={webviewRef}
/>
Enter fullscreen mode Exit fullscreen mode

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.

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;
Enter fullscreen mode Exit fullscreen mode

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

alt text

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.

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;
Enter fullscreen mode Exit fullscreen mode

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

alt text

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.

document.addEventListener("message", function(event) {
 alert(event.data)
}
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

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

alt text

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

WebView — Navigation and Data flow with WebViews in a React Native app

 git clone https://github.com/mukeshmandiwal/RNWebViewExample.git

 cd RNWebViewExample

 yarn install or npm install

# for ios
cd ios && pod install

react-native run-ios

# for android
react-native run-android
Enter fullscreen mode Exit fullscreen mode

WebView and custom navigation to handle navigation in WebView

alt text

How to Pass data to React Native from WebView

alt text

How to Pass data to WebView from React Native

alt text

More detailed explanation here






Top comments (12)

Collapse
 
digitlninja profile image
digitalninja • Edited

Hey man, great article. Ive tried to get data from RN to my WebApp but am not winning.
Where did you add the eventListener exactly?

Heres my gist
gist.github.com/digitlninja/fd4dea...

Collapse
 
mukeshmandiwal profile image
Mukesh Mandiwal • Edited

/* 
Sample data you want to pass from react  native to web 
*/

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
    `;

<WebView 
 source={{ uri: 'http://localhost:3000' }} // your webview  source
 ref={webviewRef}
 injectedJavaScript={runFirst}  // pass data to web from react native
/>

 /* In your webview now you can access using  */
<script>
  document.addEventListener("message", function(data) {
                       alert(data.data)
 });
</script>
Collapse
 
prajyotdukse profile image
prajyotdukse

Hi @mukesh,

How do I pass dynamic values from react native to web view.
Static values I am able to pass but struggling to pass dynamic token

Regards,
Prajyot

Thread Thread
 
mukeshmandiwal profile image
Mukesh Mandiwal

JSON.stringify your dynmaic token and add to injectedJavaScript = {json.stringfy(yourtoekn)}

Collapse
 
kris profile image
0xAirdropfarmer

Thank you for this awesome tutorial. UI layouts can be improved but overall a great tutorial to get an in-depth concept of React Native Webview along with React Native Navigation. Anyone going through this article can grasp the concept of utilization of webviews in React Native app.

Collapse
 
redbar0n profile image
Magne

What would you think about using this method to achieve a Hybrid-Native Sweet Spot: Native navigation with web content?

signalvnoise.com/posts/3743-hybrid...

It would allow you to make your webapp in whatever web framework you’d like, and swap it out whenever something better (like SolidJS) comes along. Clients would also get updates to the main webapp instantly. As opposed to users having to update their native app frequently, after the developer had to wait 14 days for App Store approval each time.

You could also completely avoid having an API client, merely serving HTML over the wire. See Hotwire.dev, where HOTwire Strada is similar to the JSON bridge you used to communicate between React Native and the WKWebView.

Doing that would allow you to SSR content even for the Native app. Giving more control to the server as to what is rendered, instead of using something like Server-Driven Rendering (like AirBnB etc.) which controls native components by using JSON commands from the server.

Not sure how good web animations would be, but allegedly they can run in 60fps inside the WKWebView.

This would also be superior to using CapacitorJS as a bridge between a webapp and native, since Capacitor requires you bundle and distribute everything (including updates) in the App Store.

Collapse
 
gautham495 profile image
Gautham Vijayan

Gave exactly what i was looking for!!!

Collapse
 
mohamedshrif995 profile image
mohamedshrif995

thanks it's a great tutorial
can you add how to get location if URL included 'add address' or something like that

Collapse
 
mukeshmandiwal profile image
Mukesh Mandiwal

You can use in this way


<WebView
  source={{uri: 'https://www.google.com/maps?daddr=28.69875679999999,77.29257710000002'}} />

Collapse
 
hoangdangcodecomplete profile image
hoangdangcodecomplete

Hey man, i use source uri is a web build by react js. So can you tutorial me get data from react native app to react js web through ?

Thank you!

Collapse
 
lilkizarmie profile image
Jeffery Kizarmie Adolor

been looking for this its very wonderful,

Collapse
 
venkateshparih4 profile image
venkateshparihar@gmail.com • Edited

How can we use different event type name in add event listener instead of message.because it capturing other message events also.
document.addEventListener("message"