Communicating between React Native and the WebView

Guy Blank
Guy Blank
Aug 7, 2017 · 4 min read

Our current WorkSimple app in production is a Cordova hybrid app which means that our UI is web based. We are putting in a lot of effort trying to make the user experience as smooth as possible, but it never feels as good as native apps.

For this reason, we have been pushing for React Native for a while. Once we received the go-ahead we decided that our first milestone would be to get rid of Cordova and its plugins but stay inside the WebView running within a React Native wrapper, and in the next milestone we plan on migrating our app’s React.js components to React Native.

We are happy to share some of what we have learned during the process, with a focus on working with the React Native WebView.

The first and most basic task we had was to find a way to communicate between the React Native code and the web code running in the WebView. There are three React Native WebView APIs that can be used (not including some more ways that require writing native code):

  1. injectedJavaScript: lets you inject JavaScript code to be executed within the context of the WebView.
    The problem is that this code will only run once the WebView is loaded so unless you want to reload your web content each time you want to pass something into the WebView this is not the way to go. This would be more suitable for some one time onLoad code.
    Another strange thing is that it behaves differently between iOS and Android — in iOS you can get the returned value from the injected JavaScript (as long as it’s a string), but in Android, well… you get nothing.
injectedJavaScript={'(function(){return "Send me back!"}());'}
onNavigationStateChange={(navEvent)=> console.log(navEvent.jsEvaluationValue)}

2. injectJavaScript: lets you inject JavaScript code that is executed immediately on the WebView, with no return value. We found this API to be perfect for setting globals and dispatching events on the window.

3. onMessage/postMessage: this is probably the only API that gives a real two way bridge for communication. postMessage does in its implementation something similar to injectJavaScript, only with the extra boilerplate code of firing a message event in the WebView.
Sending messages between React Native and the WebView is a matter of calling postMessage and implementing onMessage on the receiving side to get the message:

onMessage={(event)=> console.log(}

This seems simple enough, but if you’re planning on heavy usage of this communication channel you should be aware of a few caveats:

  1. There is no out of the box response mechanism for the postMessage calls (on both sides) so you would probably need to have some components that can manage some sort of callback mechanism. You can take a look at the WebViewBridge example for a sample implementation of such a component for the code running in the WebView.
  2. There is an issue in iOS when doing consecutive window.postMessage calls. The way React-Native implemented the window.postMessage in iOS is by setting a URL with a special schema and containing the data posted as a url parameter. Then in the native code, they listen for url changes and look for ones with the postMessage schema to extract the data and pass it to the onMessage handlers. The problem is that doing consecutive window.postMessage calls in iOS will result in losing your first calls as the url is overridden each time before the message is delivered so the last call wins. Luckily, a fix for this issue is on its way. In the mean time, you can checkout the WebViewBridge sample which handles this issue with a promise chain that resolves each time a message gets delivered.

Another concern we initially had with the implementation on iOS was that since the data is actually posted on the url string, can it handle large data? We tested this by passing a 20MB image file encoded as a base64 string — turns out iOS has no problem passing that successfully.
But be careful, do not do this in debug mode. When we accidentally logged the url containing this massive string to the console, the app froze up entirely — our guess is that since it is transmitted over a websocket to the chrome browser, the debug channel gets jammed.

As of now we’ve completely replaced all of the Cordova code with React Native, still utilizing a WebView to host our app. The choice of using postMessage/onMessage for communicating between React Native and the WebView has turned out to work quite well.

Update 1: A question that keeps coming up from people who read the post is :”Did you get a big performance boost?

Well getting the big performance boost wasn’t the goal of the our first mile stone in the migration to React-Native (since most of our UI & logic still live inside the WebView). If you take into account that in our pure Cordova app, calls to native used to go over the WebView bridge straight into the native code where in our current state in our React-Native app calls go over the WebView bridge and then probably (if native code is needed) over the React-Native bridge as well. So we might even experience some performance degradation at this stage.

But the Cordova FW and plugins are huge and not having to load all that code and wait for device ready did give us an improvement in load time.

Another benefit was that some Cordova code that had implementations in both iOS and Android we were able to replace with a single React-Native javascript implementation which improved our TCD.

Update 2: React Native 0.57 moves to WKWebView!

Capriza Engineering

Capriza engineering blog focused on technology, interesting challenges and related topics

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade