Should you use React Native to build your startup’s mobile app?

Co-authored by Saar Berkovich & Michael Ostrovsky.

The release of the first iPhone in 2007 caused a major disruption in the mobile phone industry, making for a gold rush that is reminiscent of the wild west and whose effects are still resonating today, 10 years later, across multiple industries.

If you wish to build a globally-recognized mobile application nowadays, you want to have it available for both Android and iOS. You can (and arguably should) launch it on one of the two platforms, but when and if you get traction users will start approaching you asking for the app to be available for their platform. This approach serves as a wake-up call that you’re missing out on 30%-70% of smartphone owners as potential users (depending on your demographic).

Around a year ago, we began evaluating React Native as a means to build a cross-platform mobile application. The development of Snipe for mobile incepted at Q2 of 2018, and so far has been based around React Native exclusively. This includes: realizing the product vision, building an MVP, releasing it on the App Store and Play Store, and building it up through regular software updates.

Background

The initial idea behind the Snipe App was to help gamers find good teammates to play with — a problem that is widespread throughout the world of competitive gaming and Esports — by revolutionizing the way they do so. No more would gamers need to rely on their luck when seeking teammates by using in-game matchmaking (where the focus lies on creating fair matches between two opposing groups), spamming Discord servers, or going through forum-based Websites. The alternative we were to offer is matchmaking for teammates, powered by AI (utilizing ML models and recommender systems technology) on the server, and a mobile application for the client, so that users can find people to play with later, throughout their day.

We’ve assembled a technical team of varied backgrounds, even so, it just so happens that our combined experience in developing mobile apps, at that point, added up to a couple of “Hello World”-esque Android apps. This, plus the desire to release both on iOS and Android, has made the promise of React Native very appealing to us.

React Native is a Facebook maintained open source toolset (MIT-licensed) that enables building cross-platform mobile apps (on iOS and Android) in JavaScript (making use of JavaScriptCore, WebKit’s JavaScript engine), based on React with web-like styling and markup syntaxes similar to CSS and HTML. It has been around since 2015 and is being used by the likes of Airbnb, Wix, and Discord, making it arguably the most mature and widely used of the cross-platform apps development solutions (other similar solutions that come to mind: Native Script, Apache Cordova, Microsoft Xamarin, and the newly released Google Flutter).

As we’ve soon discovered, building cross-platforms React Native apps from scratch does not come without its obstacles, ones that developers doing typical mobile app development seldom need to face.

The rest of this post will focus on our development experience with React Native — what we like, what we don’t, and when we recommend using it. It should be noted that we are using React Native v55 (which is fairly recent but is not the newest).


“There are two kinds of programmers in the world, those with arrogance and those that test. You test?”

The Good

React Native is Quick and Dirty-certified — In a startup environment, fast iterations are key. Other than the fact that you’re writing code for both platforms simultaneously, using an untyped language like JavaScript essentially allows pushing iterations faster (while possibly compromising on stability in the form of bugs — keep this in mind).

Being able to share programming language for both platforms meant we were able to take our existing iOS app and release it on the Android Play Store within less than a week (!). This is not something we would have been able to if the app was written in straight Swift or Objective-C.

The fact that React Native is heavily inspired and based on web technologies means that developers with a background in web will feel right at home with it. The same cannot be said for newcomers to apps development in the native SDKs, as they implement proprietary formats to design UI. This is especially relevant for organizations (and individuals) who are deeply rooted in web technologies, like Wix (who use React Native extensively).

Another advantage of using JavaScript is that it allows reusability of code across web and even desktop (using Electron or the like). While you wouldn’t be able to share the code in its entirety (mostly seeing as React Native JSX is not identical to HTML and CSS), you will at least be able to share your Model layer (in MVC terms).

The community is big and growing, this means that new libraries are constantly popping up, and older ones are becoming increasingly polished. In addition, most problems you will encounter along the way can be solved by a simple Google search. The solutions themselves may not always be simple, but the information is certainly out there.


“Programs with many layers of separation from the kernel don’t always hang. Even a filthy Android like yours has a strong CPU.”

The Bad

React Native is not completely cross-platform — There are many inconsistencies between React Native running on iOS and Android. To give a few examples:

  • Styling behaves differently. Many changes to the layout that were initially tested on one platform, ended up breaking the other, even on similar screen resolutions.
  • Many of the features and ready-to-use components provided by React Native are only available for one of the platforms. For instance, we recently wanted to add a clear button to a text input. The docs indicated to us that there is a built-in solution for iOS, which involves altering the properties of our TextInput element. Unfortunately, there was no built-in solution for Android.
  • Errors are often inconsistent, an example for which will be described in the next point.

error handling and debugging can be truly painful. Putting aside how JavaScript errors can be cryptic on their own, issues with the source map generated while packaging/transpiling cause JS errors to point to incorrect row numbers, leaving you having to go through the code guessing where the error was thrown. 
Errors relating to React Native internals (beyond the JS code) can be even more dreadful, as they’re often completely unintelligible to anyone who is not familiar with the internal workings of React Native itself.

For better or for worse, this error has made orphaned yogi children a meme around the office.

The above error is an example of an error thrown in Android when some of the JSX in the project could not be parsed by Yoga (the layout engine used by React Native). We had this error crashing Android after testing iterations developed and tested against the iOS simulator, where it was running just fine.

Installing libraries is a nightmare — wanting to accomplish any native interaction not implemented in React Native requires you to download libraries (usually through npm or GitHub), and “link” them to your respective native projects by modifying parts of the native code and build configuration files manually (or running automations that may or may not break your entire project). The linking process is very prone to error, and the more libraries you have, the harder it becomes to add more, as libraries often conflict with each other due to dependencies and former changes made for other libraries. This process is also going to be fundamentally different for your Android app and iOS app, meaning you’ll have to go through it twice.

Many of these libraries implement features that to us seem trivial to have, but are missing in React Native itself. To name a few: push notifications (!), video playback, native navigation, and maps support.

Lastly, React Native support on Android is lacking. React Native on Android is strictly worse than the iOS counterpart, it ships with an old JavaScriptCore, it has bugs that don’t exist on iOS, it uses deprecated methods to build, and it performs noticeably slower. The reason for this appears to be twofold: iOS being favored over Android in Facebook (though it’s not official), as well as Android having significantly more possible hardware configurations to optimize and cater for.

An important caveat to make is that not all of these problems are bugs in React Native, some have to do with dependencies of React Native, some with the respective OS, and many of them can be fixed (like the source map issue). Regardless, the aforementioned problems have caused massive productivity killer and time sink for us, resulting in a lot of frustration and delayed delivery.


“If you have to code, code. Don’t talk.”

The Ugly

Needing to write native code, either in the form of a hybrid app or when linking libraries (as explained above) breeds messy code and build configuration files that are full of patches and thus hard to maintain. This is especially true when the developers are not proficient in one or both of the platforms.

UI design is an additional source for revolt. Apps on Android and iOS follow different design patterns and principles known as Material Design and Human Interface Guidelines respectively. While they borrow from each other, they have different foundations and recommendations. For example, Bottom Navigation bars are very prominent in iOS apps but have been discouraged for use on Android. Google recently changed its stance and included them in Material Design, but is still directing different coloring schemes recommendation. Failure to comply with said guidelines results in poor UX, as users are used for their apps to have a have a certain look and feel.

Striving to keep one codebase for both platforms means either embracing the design guidelines of one of the platforms for both, making for an app with an alien look and feel to users on the opposing platform or building a “Frankenstein” looking app which implements both design guidelines interchangeably - arguably worse.

React Native does make it easy to code and render different versions of components based on platform, by allowing for if (Platform.OS === ’ios’) {} else {} blocks or by specifying different files entirely based on platform, but if you’re going to go through the trouble of writing almost everything twice, does it justifies putting up with the rest of the pain points mentioned? In our opinion, it does not.


The Final Shootout

Developing in React Native is a lot like watching a Western — the action scenes are fast and exciting, but the build-up to them can be slow and tedious. As of early 2019 React Native still feels immature, and ultimately does not fulfill the “cross-platform mobile development” promise, which given the differing UI design guidelines mentioned, may never be fully .resolve()’d.

We recommend using React Native to build simple apps and discardable MVPs or prototypes, or if you intend to release only on iOS, where it is much more stable (that’s how Discord is rolling). However, if you plan on going on a complex, long time project that is meant to be released on both platforms, we believe React Native is not the right solution for you.

Airbnb has recently stopped working with React Native (after being heavy users for 2 years) and wrote an in-depth, 5-part blog post about it.

As for us, we will be looking to go full native on both platforms soon(tm). At this point in time, we feel that developing our core in React Native is holding us back.