Creating an embedded browser with React-Native using react-native-webview

Liron Navon
codesight
Published in
6 min readMar 4, 2019

--

In this tutorial, I will show you how to add an embedded browser into your RN mobile app, just like facebook does in their app.
It started as a question in one of the react native groups I’m a part of, the question was basically:

How can I embed chromium in a React Native application?

The question became a conversation about the implementation, And it came to me that people have the wrong ideas about how React Native works, and specifically about the implementations of WebView and UIWebview or WKWebView.

Photo by Lisa Fotios from Pexels

Webview is actually a full blown browser implementation, for android it would be chromium, and depends on the Android version, it will be a different version of chromium. For Android, it’s actually possible to create a new browser engine from scratch (like Firefox does with Gecko).
But apple restricts the browser to only be their implementation of mobile safari, so yea, even Chrome for IOS is actually WKWebView With extra features, chrome does have a lot of optimizations and of course google’s web layer.

In short, this is what each WebView type means:

Android WebView — WebView objects allow you to display web content as part of your activity layout, but lack some of the features of fully-developed browsers. It is based on chromium.

IOS WKWebview — An object that displays interactive web content, such as for an in-app browser. It was introduced in IOS 8 and is the new standard WebView (it is the default for IOS when using react-native-webview).

IOS UIWebview — A view that embeds web content in your app. It’s a standard WebView from the UIKit, for most use cases you should consider it as the legacy WebView — it has support for legacy features that are not supported in WKWebview.

Getting started

You can check the full code in the git repository.

We are going to go over a basic browser implementation using react-native-webview, it is able to: use the search bar to go to a website or search using any search engine, go back/forward, refresh, use incognito and inject javascript which can communicate back with the React Native code, it looks like this:

Hopefully, you already have React native, expo and Android/IOS development environment setup, if not, just follow the official getting started.

expo init RNBrowser # select the blank option
cd RNBrowser
# Eject expo, since we will use native components
npm run eject # select the expo kit option
# Install and link the webview component
react-native install react-native-webview

Now You will need to go into either XCode(for IOS) or Android Studio(for Android) and build the project from there, this can take a few minutes.
Now you can run the app normally using:

npm start

Now we can create a new component, we can call it Browser.js, and change App.js to include it.

Setting up the browser

Now we can create the browser, you can see the full code in the git repository, but we will go over different parts here, let’s start off by loading a URL:

import {WebView} from "react-native-webview";# And render
<WebView source={{ uri: 'https://medium.com'}}/>

Setting up search engines and search addresses

That’s enough to display a single page, and we can allow the user to navigate to different pages by adding a TextInput, and a button to look up the website.
Since users are lazy, we can use a function to upgrade the URL from simple words, simply put, it will take the text check if it is a valid URL, or if it looks like a URL and upgrade it to HTTPS (the WebView will degrade to HTTP if no HTTPS was hit.), and if it doesn’t look like a URL, we will simply use some search engine to look it up.

// functions to search using different engines
const
searchEngines = {
'google': (uri) => `https://www.google.com/search?q=${uri}`,
'duckduckgo': (uri) => `https://duckduckgo.com/?q=${uri}`,
'bing': (uri) => `https://www.bing.com/search?q=${uri}`
};

// upgrade the url to make it easier for the user:
//
// https://www.facebook.com => https://www.facebook.com
// facebook.com => https://www.facebook.com
// facebook => https://www.google.com/search?q=facebook
function
upgradeURL(uri, searchEngine = 'google') {
const isURL = uri.split(' ').length === 1 && uri.includes('.');
if (isURL) {
if (!uri.startsWith('http')) {
return 'https://www.' + uri;
}
return uri;
}
// search for the text in the search engine
const
encodedURI = encodeURI(uri);
return searchEngines[searchEngine](encodedURI);
}

That feature exists pretty much in any browser.

Setting navigation

Now we need another feature, we would like to allow the user to go forward and back, that’s pretty simple using the ref for the WebView, but for the UI we would also want to know if he can or can’t go back/forward.
We get the state of the browser when it changes page:

// create a method to handle navigation for the component
onNavigationStateChange = (navState) => {
const {canGoForward, canGoBack, title} = navState;
this.setState({
canGoForward,
canGoBack,
title
})
};
// render
<WebView
ref={this.setBrowserRef}
source={{ uri: currentURL}}
onNavigationStateChange={this.onNavigationStateChange}
/>

Now we can use the ref for simple operations:

// go to the next page
goForward = () => {
if (browserRef && this.state.canGoForward) {
browserRef.goForward();
}
};

// go back to the last page
goBack = () => {
if (browserRef && this.state.canGoBack) {
browserRef.goBack();
}
};

// reload the page
reload = () => {
if (browserRef) {
browserRef.reload();
}
};

Setting incognito mode

For IOS we can simply pass a flag for incognito, but for Android, we can do it manually, in it’s simplest form incognito means we won’t cache pages, allow cookies and storage and location.

# our state
state = {
currentURL: url,
urlText: url,
title: '',
canGoForward: false,
canGoBack: false,
incognito: false,
config: {
allowStorage: true,
allowJavascript: true,
allowCookies: true,
allowLocation: true,
allowCaching: true,
defaultSearchEngine: 'google'
}
};
# toggle incognito mode
toggleIncognito = () => {
this.setState({
incognito: !this.state.incognito
});
this.reload()
};
# get the configurations
get config() {
const {incognito, config} = this.state;
if (incognito) {
return {
...config,
allowStorage: false,
allowCookies: false,
allowLocation: false,
allowCaching: false,
}
}
return config;
}
# render
<WebView
source={{uri: currentURL}}
thirdPartyCookiesEnabled={this.config.allowCookies}
domStorageEnabled={this.config.allowStorage}
javaScriptEnabled={this.config.allowJavascript}
geolocationEnabled={this.config.allowLocation}
cacheEnabled={this.config.allowCaching}
/>

Injecting Javascript

Sometimes we would like to track the user or add functionality to the browser like password management or simply track the browser, it’s pretty simple to do so:

# Create some javascript to inject
const injectedJavaScript = `
alert('Injected javascript!');
window.ReactNativeWebView.postMessage('injected javascript works!');
true; // note: this is required, or you'll sometimes get silent failures
`;
# Set the script in the webview and add a listener to onMessage
<WebView
onMessage={this.onBrowserMessage}
injectedJavaScript={javascriptCode}
/>
# Do something with the message
onBrowserMessage = (event) => {
console.log(event.nativeEvent.data);
};

Or we can inject more dynamically using the ref:

browserRef.injectJavaScript(javascriptCode);

Blocking pages

Maybe you would like to automatically block ad providers to prevent annoying ads, you can use a tool like EasyList for that, and set a filter for the requests:

filterRequest = (request) => {
return !request.url.includes("evil");
};
<WebView
onShouldStartLoadWithRequest={this.filterRequest}
/>

Adding styling

We might want to add some styling to make the browser experience more unique, add a loader or a custom error component, react-native-webview allows us to use two options:

<WebView
{/* loader */}
renderLoading={() => <ActivityIndicator size="large" color="#0000ff" /> }
{/* error page */}
renderError={(errorName) => <Error name={errorName} /> }
/>

Wrapping up

We are missing a few more basic features like tabs, but it’s pretty simple to do if you want to build your own browser, it’s probably easier to optimize it properly using native modules or by passing native config, but for most use cases react-native-webview is a great solution.

--

--