The complete guide to React Native WebView
Tags: reactnative, ios, android
Published: 2020-02-08 15:47:08

---
title: The complete guide to React Native WebView
published: true
date: 2020-02-05 17:00:31 UTC
tags: reactnative,ios,android
canonical_url: https://blog.logrocket.com/the-complete-guide-to-react-native-webview/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/9ro33lji384vnmpa78tt.jpeg
---
**Written by [Rajnish Rajput](https://blog.logrocket.com/author/rajnishrajput/)**✏️

In simple terms, WebView is a component that used to load webpages in your React Native app. It was formerly available out of the box in React Native, but it will now be removed from React Native core and added as a component of the [React Native Community](https://github.com/react-native-community) libraries; for more information, read [the “slimmening proposal.”](https://github.com/react-native-community/discussions-and-proposals/issues/6) So, now, you have to install the [react-native-webview](https://github.com/react-native-community/react-native-webview) library in order to use it.

## Platforms supported

I am a React Native developer, too, so I understand how important cross-platform support is for a library you plan to use in your app. Whenever I have to use a library in my app, the first question I ask is whether it’s cross-platform. You don’t need to worry with react-native-webview — it supports both iOS and Android platforms.

> **Note** : Expo support for React Native WebView started with [Expo SDK v33.0.0](https://blog.expo.io/expo-sdk-v33-0-0-is-now-available-52d1c99dfe4c).

## Getting started

First, you need to install the library by running the below command.

```jsx
// with yarn
$ yarn add react-native-webview

// with npm
$ npm install --save react-native-webview
```

Then, link the dependencies. From react-native 0.60, auto-linking will handle the linking process, but don’t forget to run `pod install`.

React Native modules that include native Objective-C, Swift, Java, or Kotlin code have to be “linked” so that the compiler knows to include them in the app.

To link it, run the below command:

```jsx
$ react-native link react-native-webview
```

#### For iOS:

If you’re using CocoaPods in the `ios/` directory, run:

```jsx
$ pod install
```

#### For Android:

If you’re using react-native-webview version ≥6.X.X, make sure AndroidX is enabled in your project by editing `android/gradle.properties` and adding the two lines below:

```jsx
android.useAndroidX=true
android.enableJetifier=true
```

> **Note** : If you ever need to uninstall React Native WebView, run `react-native unlink react-native-webview` to unlink it.

I hope you’ve successfully installed it. If you’re stuck somewhere, please refer to the [official installation guide](https://github.com/react-native-community/react-native-webview/blob/master/docs/Getting-Started.md).

Now I’ll show you useful examples of WebView, from simple to advanced, and some how-tos after that.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Basic quickstart

Let’s begin by reviewing the basic properties of WebView.

```jsx
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';

class MyWeb extends Component {
  render() {
    return (
      <WebView
        source={{ uri: 'https://logrocket.com/' }}
        style={{ marginTop: 20 }}
      />
    );
  }
}
```

First, the `source` property is used to get the content either from a URL or HTML. If you want to load a webpage by its URL, you have to pass an object with the property `uri`, as shown below:

```jsx
<WebView
  source={{ uri: 'https://logrocket.com/' }}
/>
```

## Loading inline HTML

Above, 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:

```jsx
<WebView
  originWhitelist={['*']}
  source={{ html: '<h1>Hello world</h1>' }}
/>
```

> **Note** : Setting an `html` source requires the [`originWhiteList`](https://github.com/react-native-community/react-native-webview/blob/master/docs/Reference.md#originWhiteList) property to be set to `['*']`.

### What is the `originWhitelist` property anyway?

In simple terms, the `originWhitelist` property takes control of where users can navigate in your WebView. It takes an array of strings, and the default `whitelisted` origins are `http://` and `https://`.

For example, if you want to ensure that users are only allowed to navigate to URIs that begin with `https://` or `git://`, you would implement like so:

```jsx
<WebView
  source={{ uri: 'https://logrocket.com/' }}
  originWhitelist={['https://*', 'git://*']}
/>
```

Let’s explore some other examples.

## Loading local HTML files

Sometimes you have HTML files locally along with your app that you would like to load in WebView.

In iOS, all you need to do is import the HTML file like any other asset, as shown in the below example:

```jsx
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';

const myHtmlFile = require("./my-asset-folder/local-site.html");

class MyWeb extends Component {
  render() {
    return (
      <WebView source={myHtmlFile} />
    );
  }
}
```

Very simple — exactly what you’d expected.

But in Android, you need to put your HTML files inside the Android assets directory. For example, if you want to load a file called `logrocket.html` in your Android app, you have to move it into the Android assets directory, which is `yourProject/android/src/main/assets/`.

Then you can load the HTML file, as shown in the below example:

```jsx
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';

class MyWeb extends Component {
  render() {
    return (
      <WebView source={{ uri: "file:///android_asset/logrocket.html" }} />
    );
  }
}
```

## Controlling navigation state changes by user

`onNavigationStateChange` is a function that is invoked when the `WebView` loading starts or ends.

If you need to control the navigation state changes and do something different than navigating there in the WebView, this is the perfect way to do that. Here’s an example:

```jsx
import React, { Component } from 'react';
import { WebView } from 'react-native-webview';

class MyWeb extends Component {
  webview = null;

  render() {
    return (
      <WebView
        ref={ref => (this.webview = ref)}
        source={{ uri: 'https://logrocket.com/' }}
        onNavigationStateChange={this.handleWebViewNavigationStateChange}
      />
    );
  }

  handleWebViewNavigationStateChange = newNavState => {
    // newNavState looks something like this:
    // {
    //   url?: string;
    //   title?: string;
    //   loading?: boolean;
    //   canGoBack?: boolean;
    //   canGoForward?: boolean;
    // }
    const { url } = newNavState;
    if (!url) return;

    // handle certain doctypes
    if (url.includes('.pdf')) {
      this.webview.stopLoading();
      // open a modal with the PDF viewer
    }

    // one way to handle a successful form submit is via query strings
    if (url.includes('?message=success')) {
      this.webview.stopLoading();
      // maybe close this view?
    }

    // one way to handle errors is via query string
    if (url.includes('?errors=true')) {
      this.webview.stopLoading();
    }

    // redirect somewhere else
    if (url.includes('google.com')) {
      const newURL = 'https://logrocket.com/';
      const redirectTo = 'window.location = "' + newURL + '"';
      this.webview.injectJavaScript(redirectTo);
    }
  };
}
```

Note that this method will not be invoked on hash URL changes (e.g., from [`https://logrocket.com/users#list`](https://logrocket.com/users#list) to [`https://logrocket.com/users#help`](https://logrocket.com/users#help)). However, since version 8.0.0, if you use `onNavigationStateChange` on iOS specifically, it will now trigger on # changes to the URL.

## How to add support for file uploads

You’ll need to add the below permissions if you want to allow file uploads in WebView. I separated them by task and platform below, so you can choose exactly what you need.

#### For iOS:

If you are using iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file.

For photo capture:

```jsx
<key>NSCameraUsageDescription</key>
<string>Take pictures for certain activities</string>
```

For gallery selection:

```jsx
<key>NSPhotoLibraryUsageDescription</key>
<string>Select pictures for certain activities</string>
```

For video recording:

```jsx
<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for recording videos</string>
```

#### For Android:

Add permission in `AndroidManifest.xml`. located at `/android/app/src/main/AndroidManifest.xml`.

When I started Android development, I had real trouble finding those files in my project, which is why I added those file locations.

```jsx
<manifest ...>
  ......

  <!-- this is required only for Android 4.1-5.1 (api 16-22)  -->
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  ......
</manifest>
```

File upload using `<input type="file" />` is not supported for Android 4.4 KitKat ([see details here](https://github.com/delight-im/Android-AdvancedWebView/issues/4#issuecomment-70372146)), but if you want to check whether the file upload is supported, you can check it using `isFileUploadSupported()`. See the example below:

```jsx
WebView.isFileUploadSupported().then(res => {
  if (res === true) {
    // file upload is supported
  } else {
    // file upload is not supported
  }
});
```

## Controlling multiple file uploads

In any case, if you want to control single and multiple file uploads, you can simply add the [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#multiple) attribute on your `input` element, as shown in the below example:

```jsx
// multiple file selection
<input type="file" multiple />

// single file selection
<input type="file" />
```

## Adding support for file downloads

Yes, if you want your users to be able to download files from your WebView, you need to add permission for that.

#### For iOS:

For iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file.

Save to gallery:

```jsx
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Save pictures for certain activities.</string>
```

#### For Android:

Add permission in `AndroidManifest.xml`, which, again, is located at `/android/app/src/main/AndroidManifest.xml`:

```jsx
<manifest ...>
  ......

  <!-- this is required to save files on Android  -->
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  ......
</manifest>
```

## How to inject JavaScript in WebView

Sometimes you will find yourself in a situation where you want to run JavaScript with the WebView. For that case, WebView provides you with three different methods:

1. The `injectedJavaScript` prop
2. The `injectJavaScript` method
3. The `postMessage` method and `onMessage` prop

### The `injectedJavaScript` prop

This method runs your provided script immediately after the webpage loads for the first time. It only runs once, even if the page is reloaded or the user navigates away. For example:

```jsx
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const myScript = `
      document.body.style.backgroundColor = 'red';
      setTimeout(function() { window.alert('hi') }, 2000);
      true; // note: this is required, or you'll sometimes get silent failures
    `;
    return (
      <View style={{ flex: 1 }}>
        <WebView
          source={{
            uri:
              'https://github.com/react-native-community/react-native-webview',
          }}
          injectedJavaScript={runFirst}
        />
      </View>
    );
  }
}
```

> **Note** : I don’t know why, but as its official documentation says, `true;` is required at the end of the script. If you don’t use it, you will sometimes get silent failures, so it’s just better to include it.

So, in the above script, I set the background color to red and alert `hi` after two seconds, as you can see in the below image. `myScript` will run once the page is loaded.

![injectedJavaScript Prop In Action](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/injectedJavaScript-prop-example.png?resize=600%2C1032&ssl=1)

#### What’s going on internally?

In iOS, `injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:`. On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`.

As we mentioned, the `injectedJavaScript` prop runs after content load. But what if you need to run the JavaScript code before loading the content?

For that we have another prop called `injectedJavaScriptBeforeContentLoaded` that runs your JavaScript code before the page loads for the first time. It only runs once, even if the page is reloaded or the user navigates away. You can use this prop when you want to inject anything into the window, `localStorage`, or document prior to the code executing.

```jsx
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const runFirst = `
      window.isNativeApp = true;
      true; // note: this is required, or you'll sometimes get silent failures
    `;
    return (
      <View style={{ flex: 1 }}>
        <WebView
          source={{ uri: 'https://logrocket.com/' }}
          injectedJavaScriptBeforeContentLoaded={runFirst}
        />
      </View>
    );
  }
}
```

### The `injectJavaScript` method

The downside of the `injectedJavaScript` prop is that it only runs once. That’s why they also expose a method on the WebView ref called `injectJavaScript`. (Note the little difference in name!)

This is how you can use the `injectJavaScript` prop:

```jsx
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const run = `
      document.body.style.backgroundColor = 'blue';
      true;
    `;

    setTimeout(() => {
      this.webref.injectJavaScript(run);
    }, 3000);

    return (
      <View style={{ flex: 1 }}>
        <WebView
          ref={r => (this.webref = r)}
          source={{ uri: 'https://logrocket.com/' }}
        />
      </View>
    );
  }
}
```

After three seconds, the code will run and make the background blue:

![injectJavaScript Method In Action](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/injectJavaScript-method-example.png?resize=600%2C1032&ssl=1)

#### What’s going on internally?

On iOS, `injectJavaScript` calls WebView’s `evaluateJS:andThen:`. On Android, `injectJavaScript` calls Android WebView’s `evaluateJavascriptWithFallback` method.

### The `window.ReactNativeWebView.postMessage` method and `onMessage` prop

Well, the previous approaches are really helpful with injecting JavaScript code using props. But what if the webpage wants to send/communicate something back to your React Native code? That is where you can use `window.ReactNativeWebView.postMessage` and the `onMessage` prop.

You must set `onMessage` or the `window.ReactNativeWebView.postMessage` method will not be injected into the webpage.

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

```jsx
import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    const html = `
      <html>
      <head></head>
      <body>
        <script>
          setTimeout(function () {
            window.ReactNativeWebView.postMessage("Hello!")
          }, 2000)
        </script>
      </body>
      </html>
    `;

    return (
      <View style={{ flex: 1 }}>
        <WebView
          source={{ html }}
          onMessage={event => {
            alert(event.nativeEvent.data);
          }}
        />
      </View>
    );
  }
}
```

The result of the code is a `Hello!`, alert as depicted in the image below:

![onMessage Prop Example](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/onMessage-prop-example.png?resize=600%2C1032&ssl=1)

## Conclusion

If you read this whole article, I can say that you probably know more than you’re gonna use in React Native WebView — and I think it’s always better to know more.

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [The complete guide to React Native WebView](https://blog.logrocket.com/the-complete-guide-to-react-native-webview/) appeared first on [LogRocket Blog](https://blog.logrocket.com).