# React Native WebView Guide This document walks you through the most common use cases for React Native WebView. It doesn't cover [the full API](Reference.md), but after reading it and looking at the sample code snippets you should have a good sense for how the WebView works and common patterns for using the WebView. _This guide is currently a work in progress._ ## Guide Index - [Basic Inline HTML](Guide.md#basic-inline-html) - [Basic URL Source](Guide.md#basic-url-source) - [Controlling navigation state changes](Guide.md#controlling-navigation-state-changes) - [Add support for File Upload](Guide.md#add-support-for-file-upload) - [Multiple files upload](Guide.md#multiple-files-upload) - [Add support for File Download](Guide.md#add-support-for-file-download) - [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) - [Working with custom headers, sessions, and cookies](Guide.md#working-with-custom-headers-sessions-and-cookies) ### Basic inline HTML The simplest way to use the WebView is to simply pipe in the HTML you want to display. Note that setting an `html` source requires the [originWhiteList](Reference.md#originWhiteList) property to be set to `['*']`. ```js import React, { Component } from 'react'; import { WebView } from 'react-native-webview'; class MyInlineWeb extends Component { render() { return ( This is a static HTML source!' }} /> ); } } ``` Passing a new static html source will cause the WebView to rerender. ### Basic URL Source This is the most common use-case for WebView. ```js import React, { Component } from 'react'; import { WebView } from 'react-native-webview'; class MyWeb extends Component { render() { return ( ); } } ``` ### Controlling navigation state changes Sometimes you want to intercept a user tapping on a link in your webview and do something different than navigating there in the webview. Here's some example code on how you might do that using the `onNavigationStateChange` function. ```js import React, { Component } from 'react'; import { WebView } from 'react-native-webview'; class MyWeb extends Component { webview = null; render() { return ( (this.webview = ref)} source={{ uri: 'https://facebook.github.io/react-native/' }} 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://facebook.github.io/react-native/'; const redirectTo = 'window.location = "' + newURL + '"'; this.webview.injectJavaScript(redirectTo); } }; } ``` #### Intercepting hash URL changes While `onNavigationStateChange` will trigger on URL changes, it does not trigger when only the hash URL ("anchor") changes, e.g. from `https://example.com/users#list` to `https://example.com/users#help`. You can inject some JavaScript to wrap the history functions in order to intercept these hash URL changes. ```jsx { if (state.data === 'navigationStateChange') { // Navigation state updated, can check state.canGoBack, etc. } }} /> ``` Thanks to [Janic Duplessis](https://github.com/react-native-community/react-native-webview/issues/24#issuecomment-483956651) for this workaround. ### Add support for File Upload ##### iOS For iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file: Photo capture: ``` NSCameraUsageDescription Take pictures for certain activities ``` Gallery selection: ``` NSPhotoLibraryUsageDescription Select pictures for certain activities ``` Video recording: ``` NSMicrophoneUsageDescription Need microphone access for recording videos ``` ##### Android Add permission in AndroidManifest.xml: ```xml ...... ...... ``` ##### Check for File Upload support, with `static isFileUploadSupported()` File Upload using `` is not supported for Android 4.4 KitKat (see [details](https://github.com/delight-im/Android-AdvancedWebView/issues/4#issuecomment-70372146)): ``` import { WebView } from "react-native-webview"; WebView.isFileUploadSupported().then(res => { if (res === true) { // file upload is supported } else { // not file upload support } }); ``` ### Multiple Files Upload You can control **single** or **multiple** file selection by specifing the [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#multiple) attribute on your `input` element: ``` // multiple file selection // single file selection ``` ### Add support for File Download ##### iOS For iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file: Save to gallery: ``` NSPhotoLibraryAddUsageDescription Save pictures for certain activities. ``` ##### Android Add permission in AndroidManifest.xml: ```xml ...... ...... ``` ### Communicating between JS and Native You will often find yourself wanting to send messages to the web pages loaded by your webviews and also receiving messages back from those web pages. To accomplish this, React Native WebView exposes three different options: 1. React Native -> Web: The `injectedJavaScript` prop 2. React Native -> Web: The `injectJavaScript` method 3. Web -> React Native: The `postMessage` method and `onMessage` prop #### The `injectedJavaScript` prop This is a script that runs immediately after the web page loads for the first time. It only runs once, even if the page is reloaded or navigated away. ```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 = ` document.body.style.backgroundColor = 'red'; setTimeout(function() { window.alert('hi') }, 2000); true; // note: this is required, or you'll sometimes get silent failures `; return ( ); } } ``` This runs the JavaScript in the `runFirst` string once the page is loaded. In this case, you can see that both the body style was changed to red and the alert showed up after 2 seconds. screenshot of Github repo _Under the hood_ > On iOS, `injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:` > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback` #### The `injectJavaScript` method While convenient, the downside to the previously mentioned `injectedJavaScript` prop is that it only runs once. That's why we also expose a method on the webview ref called `injectJavaScript` (note the slightly different name!). ```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 ( (this.webref = r)} source={{ uri: 'https://github.com/react-native-community/react-native-webview', }} /> ); } } ``` After 3 seconds, this code turns the background blue: Screenshot of app showing injected javascript _Under the hood_ > 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 Being able to send JavaScript to the web page is great, but what about when the web page wants to communicate back to your React Native code? This is where `window.ReactNativeWebView.postMessage` and the `onMessage` prop come in. You _must_ set `onMessage` or the `window.ReactNativeWebView.postMessage` method will not be injected into the web page. `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 = ` `; return ( { alert(event.nativeEvent.data); }} /> ); } } ``` This code will result in this alert: Alert showing communication from web page to React Native ### Working with custom headers, sessions, and cookies #### Setting Custom Headers In React Native WebView, you can set a custom header like this: ```jsx ``` This will set the header on the first load, but not on subsequent page navigations. In order to work around this, you can track the current URL, intercept new page loads, and navigate to them yourself ([original credit for this technique to Chirag Shah from Big Binary](https://blog.bigbinary.com/2016/07/26/passing-request-headers-on-each-webview-request-in-react-native.html)): ```jsx const CustomHeaderWebView = props => { const { uri, onLoadStart, ...restProps } = props; const [currentURI, setURI] = useState(props.source.uri); const newSource = { ...props.source, uri: currentURI }; return ( { // If we're loading the current URI, allow it to load if (request.url === currentURI) return true; // We're loading a new URL -- change state first setURI(request.url); return false; }} /> ); }; ; ``` #### Managing Cookies You can set cookies on the React Native side using the [react-native-cookies](https://github.com/joeferraro/react-native-cookies) package. When you do, you'll likely want to enable the [sharedCookiesEnabled](Reference#sharedCookiesEnabled) prop as well. ```jsx const App = () => { return ( ); }; ``` If you'd like to send custom cookies in the WebView itself, you can do so in a custom header, like this: ```jsx const App = () => { return ( ); }; ``` Note that these cookies will only be sent on the first request unless you use the technique above for [setting custom headers on each page load](#Setting-Custom-Headers).