The Duel : React Native vs. Cordova

As a result of smartphones and mobile applications becoming so popular, web developers have been looking for ways to create mobile applications using JavaScript. This popularity resulted in the development of many JavaScript frameworks capable of running native-like applications on mobile devices. Currently, Cordova and React Native are the most popular choices. Cordova supports iOS, Android, and Windows Phone mobile platforms. With React Native, on the other hand, Android, iOS, and UWP are targets for developers. (UWP stands for Universal Windows Platform, Microsoft’s platform that allows the same application to run on Windows Phone 10 Mobile, XBox One and Windows 10.)

From the surface, it appears that React Native and Cordova are occupying the same space. However, as with all technologies, there are aspects where one shines, and the other falls short. So, to get a better picture of each technology, and to learn their advantages and pitfalls, we will dive into details of each and compare them across different disciplines.

Differences in Philosophy

It’s important to remember that React Native’s tagline, “Learn once, write anywhere” differs from the usual cross-platform mantra, “Write once, run anywhere.” This leads to two things: Firstly, we can’t just take our existing React codebase from our web project and turn it into a mobile application in just a few clicks. However, React and React Native do share a lot of key concepts with one example being their component systems, and as a result, React Native feels instantly familiar. While React shares a lot of similarities with React Native, there are some core differences, which ranges from the way stylesheets are handled to the type of components we can use.

Secondly, we might find ourselves not able to share React Native code when targeting different platforms. This happens when we would rather have user interface elements behave natively to their specific platform, in turn giving the user a better experience and a more native feel to the app. An obvious example is the drawer side menu in Android apps, which is very uncommon in iOS apps.

Cordova does not share this philosophy. It is not uncommon to start developing a pure web application, later bundle it as a Cordova application, and reuse as much code as possible for all the (mobile) platforms we want to target.

Development Freedom

On mobile devices, Cordova is running a single-page application inside the integrated mobile web browser, called WebView, and then wrapping that as a native application. While it looks like a native application from the outside, our web code is running inside the mobile browser engine. For us, that means we are not tied to a specific library or framework. If we are using vanilla JavaScript, jQuery, Angular or anything else, any of those options could be bundled into a mobile application with Cordova. Cordova does not impose on our technology stack. As long as we have an index.html file, we are good to go. One simple example would be the following code snippet:

<html>
  <head>
    <title>My Cordova App</title>
  </head>
  <body>
    <div id="tapme">Tap me</div>
    <script>
      // Select our element
      var element = document.getElementById('tapme');
      
      // Send an alert once it was tapped/clicked
      element.addEventListener('click', function() {
        alert('Hello there!');
      });
    </script>
  </body>
</html>

This example means we can use pretty much anything we desire, such as using a package manager like NPM or Bower, using a transpiler such as Babel, CoffeeScript or TypeScript, a bundler like Webpack or Rollup, or something else entirely. It doesn’t matter, as long as the result is an index.html file that loads all the JavaScript and stylesheets we need.

React Native, as the name implies, builds upon React. It’s important to understand the React part in React Native is one of its core features. If you are not a fan of React’s declarative nature, including JSX, its componentization, and data flow, chances are you are probably not going to be happy with React Native. While React Native instantly feels familiar to React developers, at first sight, there are some differences to remember. With React Native we don’t have any HTML or CSS. Instead, this technology is focused on the JavaScript side. As an alternative to CSS, styles are being written inline, and Flexbox is the default styling model.

The most barebones React Native application would look similar to this example:

// Import the React module for JSX conversion
import { React } from 'react';
// Import React Native's components
import {
  View,
  Text,
  AppRegistry,
  TouchableOpacity,
} from 'react-native';

// Create an App component
const App = () => {
  // Define our press handler
  const onPress = () => alert('Hello there!');
  
  // Compose the components we are going to render
  return (
    <View>
      <TouchableOpacity onPress={onPress} />
        <Text>Tap me!</Text>
      </TouchableOpacity>
    </View>
  );
};

// Registers the `App` component as our main entry point
AppRegistry.registerComponent('App', () => App);

React Native has its own packager. It bundles all the JavaScript files into one giant file, which is then consumed and executed by JavaScriptCore, Apple’s JavaScript engine. JavaScriptCore is being used on iOS and Android, while ChakraCore is powering React Native UWP applications. By default, React Native uses the JavaScript transpiler Babel, allowing us to use ECMAScript 2015+ (ECMAScript 6) syntax. While it’s not necessary to use ECMAScript 2015+ syntax, it is definitely encouraged, as all the official examples and third-party modules are embracing it. Since React Native is taking care of the packaging and transpiling process, our application code and third-party modules can take advantage of these features without the need to configure the tools for ourselves.

To sum up, React Native is a React-centric opinionated approach to mobile development, while Cordova allows us to bundle web technologies inside the WebView shell.

Native Look and Feel

One thing that is important to users is to have a native look and feel of an application. Since Cordova applications are usually simple web applications, there are a few things that might feel strange at first. Problems may range from missing visual feedback on tap areas, to scrolling that doesn’t feel as silky smooth as in native applications, to there being a 300-millisecond delay on tap events. While there are solutions for all of these issues, we should remember that we may need to put in some extra effort if we want our Cordova application to feel as close to native applications as possible. In Cordova, we don’t have access to any native controls. If we want to have a native look and feel, we are left with two options: Either re-create the native controls, such as buttons and input elements, with HTML and CSS, or implement native modules that directly access those native controls. We could do this by ourselves or by using a third-party library such as Ionic or Onsen UI. Note, it’s important to keep them up-to-date with OS updates as they come along. Sometimes, the look of a mobile operating system gets a facelift, as happened when iOS 7 was introduced. Having an app that it isn’t able to adapt will take users out of the experience. We could also resort to including Cordova plugins that connect us to the native side of things. One of the most complete native controls is Microsoft’s Ace library.

With React Native, on the other hand, we have access to native controls and interaction out of the box. Components such as TextTextInput or Slider map to its native counterparts. While some components are available for all platforms, other components only work on specific platforms. The closer we want our application to have a native look and feel, the more we need to use components that are only available for this specific platform and so the more our codebase diverges. Mind touch interactions and gestures are part of React Native as well.

Comparing Performance

With Cordova having only a WebView at its disposal, we are bound to the limitations of the WebView. For example, following its 4.0 version, Android finally started using (the much faster) Chrome engine as the default WebView. While with iOS, for long the application running inside the default WebView engine was significantly slower than the same application in the Safari mobile browser. Furthermore, since JavaScript is single threaded, we may run into issues if there are too many things going on in our application code. These limitations lead to sluggish animations, and our application may not feel as responsive as we would like it to be. While there may be some tricks we can employ here and there, in the end, we are bound by the mobile browser’s limits.

React Native utilizes multiple threads, therefore rendering UI elements run in their own thread. Because React components link to native views, JavaScript is not doing the heavy lifting in React Native.

Developer Workflow

Cordova offers a command-line utility to create new project templates, starting the application in the simulator and building the application for the actual device in a production mode. Most of the time, we are developing the application on a desktop browser and may later bundle it up as a mobile application. With the freedom Cordova offers, we need to tackle the development workflow ourselves. If we want live-reloading on the device, we need to implement it ourselves. To debug Cordova applications, we apply the same principles used to debug a website. In iOS, for example, we would connect our mobile device via USB, open Safari and its developer tools.

React Native offers a similar command-line interface and offers a development workflow familiar to web developers. We get live reloading out of the box. Once we change a React component, our application reloads with the changes we made. One of the most exciting features is hot module replacement, which partially reloads the changes in the component we made, without altering the application’s state. We could even connect to an actual device and see if our changes work as we would expect them to on a real device. Our React Native applications can be debugged remotely with Chrome for Desktop. Error handling is obvious in React Native; if we run into an error, our application displays a red background, and the stack trace is shown. Thanks to sourcemaps, we can see the error’s exact location. When we click on it, our editor of choice opens up at the code’s precise location.

Extensibility and Access to the Native Features

From the JavaScript side of things, we are free to use any JavaScript library, including packages from NPM. However, because React Native is not a browser environment, we may find it difficult utilizing code that relies on DOM. React Native embraces CommonJS and ES2015 modules, so any libraries using these formats are easy to integrate.

Both Cordova and React Native have the ability to create and use plugins that connect to the native side of things. Cordova provides a low-level API for creating our own, which give us a lot of control, but leads to the use of more native and JavaScript boilerplates.

If we were to hypothetically write a Cordova iOS plugin in Objective-C, it might look like the next code snippet. Our plugin will just log the input parameter.

#import <Cordova/CDVPlugin.h>

// Create a class that inherits from CDVPlugin
@interface Log : CDVPlugin
- (void)log:(CDVInvokedUrlCommand*)command;
@end

// The actual implementation of the class we just defined
@implementation Log

- (void)log:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult = nil;
    
    // We are getting all parameters and taking the first one
    NSString* echo = [command.arguments objectAtIndex:0];

    // We are checking for the validity of the parameters
    if (echo != nil && [echo length] > 0) {
        // We are just printing the parameter using the native log method
        NSLog(echo);
      
        // Let's create a result for the plugin
        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
    }

    // Let's send a signal back with the plugin's result
    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

@end

In order to use the module, this piece of JavaScript code will help:

window.log = function(str, callback) {
    cordova.exec(callback, function(err) {
        callback('Nothing to echo.');
    }, "Log", "log", [str]);
};

To use the plugin, we just need to call the log function:

window.log('Hello native!');

React Native, on the other hand, follows a different philosophy; it automatically maps JavaScript types to its native counterparts when writing plugins, which makes it easier to connect native code with JavaScript. Let’s take a look at a piece of code that’s necessary for creating a native module with React Native:

#import "RCTBridgeModule.h"

@interface Log : NSObject <RCTBridgeModule>
@end

@implementation Log

RCT_EXPORT_MODULE();

// This makes this method available NativeModules.Log.log
RCT_EXPORT_METHOD(log:(NSString *)message)
{
  NSLog(message);
}
@end

React Native binds the module for us with the calls RCT_EXPORT_MODULE and RCT_EXPORT_METHOD. We can now access it with NativeModules.Log.log as so:

import { React } from 'react';
import {
  View,
  Text,
  AppRegistry,
  NativeModules
  TouchableOpacity,
} from 'react-native';

// Create an App component
const App = () => {
  // Log with our module once we tap the text
  const onPress = () => NativeModules.Log.log('Hello there');
  
  return (
    <View>
      <TouchableOpacity onPress={onPress} />
        <Text>Tap me!</Text>
      </TouchableOpacity>
    </View>
  );
};

// Registers the `App` component as our main entry point
AppRegistry.registerComponent('App', () => App);

While we only took a close look at creating a module in iOS using Objective-C, the same principles apply for creating a module for Android using Java.

We need to link native plugins within the project files for each platform. With iOS, for example, this means we must link the compiled native part with our application and add the corresponding header files. This can be a long process, especially if there are a lot of native modules. Fortunately, this is significantly simplified by using a command-line utility called rnpm that has become part of React Native itself.

Conclusion: React Native or Cordova?

React Native and Cordova have different purposes and so cater to different needs. Therefore, it’s hard to say that one technology is better than the other across all disciplines.

By using Cordova, you can quickly turn your existing single page application into a mobile application for different platforms, at the cost of interactions not necessarily have the native feeling to their specific platform.

With React Native, applications have a more native look and feel but at the cost of reimplementing pieces of code for certain target platforms. If you have already dabbled in React and are interested in developing mobile applications, React Native feels like a natural extension.

Loading