A WebView is an embedded browser that can be used to display web pages inside your React Native applications. It can display anything (from custom HTML elements to entire web applications) inside React Native.
In React Native, we can use WebView by using a third-party package called react-native-webview. This is the official implementation of WebView after it was removed from the React native core to keep the core as lean as possible.
In this shot, we’re going to implement a simple project to see how to work with WebView on React Native and what some of the good use cases that it serves are.
To get started, we need to begin a new project with the React Native CLI. You can do this with Expo if you don’t want to go through the tedious installation process.
npx react-native init ExperimentingWithWebview
With Expo: If you’re using Expo, you can jump straight to Implementing a basic WebView after this:
expo install react-native->webview
For npm users:
npm install react-native-webview
If you’re using yarn:
yarn add react-native-webview
You’ll only need this if you’re not using Expo and using React Native CLI to start your project.
react-native link react-native-webview
If you’re using
react-native
≥ 0.60, auto-linking will take care of this step so feel free to skip, but don’t forget to runpod install
.
If you ever uninstall this package:
react-native unlink react-native-webview
If you’re using cocoapods in the ios/
or macos/
directory, run:
pod install
If you’re using react-native-webview < 6: Feel free to skip.
If you’re using react-native-webview ≥ 6.x.x:
Make sure AndroidX is enabled in your project by editing android/gradle.properties
.
android.useAndroidX=true
android.enableJetifier=true
Chances are that this has been automatically done by React Native CLI.
It’s time to get rid of the boilerplate code inside app.js and write our code for the WebView:
// App.js
import React from 'react';
import {SafeAreaView, StatusBar} from 'react-native';
import {WebView} from 'react-native-webview';
const App = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView 1}}>
<WebView source={{uri: 'https://www.educative.io/'}} />
</SafeAreaView>
</>
);
};
export default App;
This is all you need to do in order to display a website in your application!
originWhitelist
propThis is a list of origin strings that you are allowed to navigate inside the webview component, which takes an array of strings. By default, its values are http://
and https://
. If the user navigates to a new page that isn’t in whitelisted origins, the URL will be handled by the OS.
<WebView
source={{ uri: 'https://www.educative.io/' }}
originWhitelist={['https://*', 'git://*']}
/>
You can also load local or inline HTML files into a WebView. This is how you can achieve that:
const sourceHtml = require("./src/index.html"); // your html file here
export const App = () => <WebView source={{ html: sourceHtml }} />
Or you can add some inline HTML:
export const App = () => {
const htmlSource =
'<h1>Hello World 🌍</h1>';
return (
<SafeAreaView 1}}>
<WebView source={{ html: htmlSource }} />
</SafeAreaView>
)
}
You can also add your own JavaScript, that can run inside the WebView, to manipulate the behavior of the website you’re loading according to your needs.
There are two ways to do this:
injectedJavascript
propinjectJavascript
methodI know these names can be confusing, so we will understand them based on their purpose. It’ll be clear to you in a few examples.
injectedJavascript
propThis is a prop to the WebView component that contains the scripts that will run once the web page loads for the first time. The prop only runs once, even if the web page is reloaded or the user navigates away.
import React from 'react';
import {View} from 'react-native';
import {WebView} from 'react-native-webview';
const App = () => {
const scripts = `
document.body.style.backgroundColor = 'hotpink';
document.querySelector("h1").style.color = 'skyblue';
document.querySelector("p").style.padding = '20px';
true;
`;
return (
<View 1}}>
<WebView
source={{uri: 'https://www.example.com/'}}
injectedJavaScript={scripts}
/>
</View>
);
};
export default App;
injectJavascript
methodAs we know, the injectedJavascript
prop that we discussed above only works on the first load – there is no other way to do this if we want to run scripts or page navigations multiple times. That’s why WebView exposes the injectJavascript
method on its reference.
import React from 'react';
import {View} from 'react-native';
import {WebView} from 'react-native-webview';
const getRandomColor = () => {
let letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
class App extends React.Component {
render() {
const scripts = () => {
const color = getRandomColor();
return `
document.body.style.backgroundColor = '${color}';
document.body.style.color = '${color}';
document.body.querySelector("a").style.color = '${color}';
true;
`;
};
setInterval(() => {
this.webref.injectJavaScript(scripts());
}, 500);
return (
<View 1}}>
<WebView
ref={r => (this.webref = r)}
source={{uri: 'https://www.example.com'}}
/>
</View>
);
}
}
export default App;
While all these ways can help us manipulate the WebView according to our needs, what if you need to send or receive data from the webpage inside WebView?
Well, let’s how we can communicate between our JS and React Native app.
This requires the use of two things:
window.ReactNativeWebView.postMessage
functiononMessage
propThe window.ReactNativeWebView.postMessage
is a function that’s exposed to the injected scripts inside WebView. We can basically pass anything to it, provided it’s properly serialized. This is clearly shown in the example below.
Rest assured, whatever we pass inside the postMessage
can be accessed in React Native through the callback passed into the onMessage
proponMessage
is a prop for the WebView that takes a callback and is triggered anytime a message is sent using postMessage
import React from 'react';
import {View} from 'react-native';
import {WebView} from 'react-native-webview';
const getRandomColor = () => {
let letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
class App extends React.Component {
render() {
const scripts = () => {
const color = getRandomColor();
return `
document.body.style.backgroundColor = '${color}';
document.querySelector("h1").style.color = 'hotpink';
window.ReactNativeWebView.postMessage(document.body.style.backgroundColor);
true;
`;
};
setInterval(() => {
this.webref.injectJavaScript(scripts());
}, 500);
return (
<View 1}}>
<WebView
ref={r => (this.webref = r)}
source={{uri: 'https://www.example.com'}}
onMessage={event => console.log(event.nativeEvent.data)}
/>
</View>
);
}
}
export default App;
This simply logs the background color of the webpage whenever it’s changed. As you can see, we are passing the callback inside the onMessage
prop that gets triggered on the postMessage
.
The actual event object that’s passed inside the onMessage
callback is this: [nativeEvent.data](http://nativeevent.data)
and the argument we passed inside window.ReactNativeWebView.postMessage
.
{
"nativeEvent": {
"canGoBack": false,
"canGoForward": false,
"data": "rgb(247, 75, 142)",
"loading": false,
"target": 15,
"title": "Example Domain",
"url": "https://www.example.com/"
}
}
Now, we’ll discuss how you can intercept whenever a user navigates inside the WebView to a different webpage or clicks on any link. You can place a callback that’ll happen anytime the route changes.
onNavigationStateChange
propThe onNavigationStateChange
prop takes a callback that is triggered whenever the user navigates inside the WebView. It prop passes the current navigation state as an argument that can be accessed inside the callback.
import React from 'react';
import {View} from 'react-native';
import {WebView} from 'react-native-webview';
class App extends React.Component {
handleNavigationStateChanged = navState => {
const {url, title} = navState;
console.log({url, title});
};
render() {
return (
<View 1}}>
<WebView
ref={r => (this.webref = r)}
source={{uri: 'https://www.example.com'}}
onNavigationStateChange={this.handleNavigationStateChanged}
/>
</View>
);
}
}
export default App;
The basic navState
param structure passed into handleNavigationStateChanged
:
{
"canGoBack": false,
"canGoForward": false,
"loading": false,
"target": 3,
"title": "Example Domain",
"url": "https://www.example.com/"
}
As a cool project, you can play around with WebView and use its goForward
and goBack
, stopLoading
injectJavascript
method above).
You can: