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

Liron Navon
Mar 4, 2019 · 6 min read

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.

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'}}/>

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.

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();
}
};

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}
/>

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

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}
/>

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.

codesight

A development blog

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store