Written by James Rickett from our Mobile App dev team.
It was in the Spring of 2016 and I was working as an iOS developer at TwoPi Code when a coworker asked if I’d used React Native before. I said I hadn’t, but I’d heard about it and was willing to try it out.
React Native is a “cross-platform” framework allows you to create both Android and iOS apps with a single codebase. It’s an alternative to “native” frameworks for iOS and Android which are the frameworks that the vendors (Apple / Google) of the platforms suggest you use. So even though “native” is in the name, I don’t consider React Native to be “native”, it is an abstraction layer on top of these frameworks.
The idea was that we were using React for web development, so if we used React Native for mobile development we’d have a more integrated team developing off a similar technology stack, and it would be easier for web developers to get into mobile development and vice versa. We were a small team, so the added flexibility in resourcing seemed like a worthwhile benefit.
I spent the next couple of weeks familiarising myself with React Native, going through any tutorials I could find. After getting to grips with the basics, I started to work on some relatively straightforward tasks. I was able to fix bugs and make additions, but it definitely felt like a much slower process. I put this down to no previous experience with either React Native or React. It is definitely a different approach to mobile app development.
Unfortunately it never really got much better. There were some bursts of productivity gains when a feature could be implemented on both platforms at the same time with the same code, but progress was generally slow and when something broke it was so difficult to determine what, where, and why. These issues probably took up 10x the amount time we saved elsewhere.
At one stage in development of the app the question was posed, “should we rewrite this natively?” I was totally onboard with that idea, but we had made a significant amount of progress, so a rewrite wasn’t really viable. We continued with the React Native approach and after couple of months, a lot of weird bugs, a lot of yelling, later we were eventually able to complete the project.
For the next few years we made a number of apps in React Native. We figured we’d be able to build on our past experiences, the experience would get better as the platform improved and that common technology stack would provide long term benefits.
Unfortunately things didn’t really improve with experience. Upgrading to newer versions of React Native was a pain. Third party frameworks would often stop receiving any updates. Functionality that you took for granted on native platforms didn’t exist. It was exhausting.
Eventually we made the decision to switch back to doing mobile apps natively. That was the best decision we ever made. For the next mobile project we developed separate Android and iOS apps and it was the most frictionless project we’d worked on in ages. Productivity increased, developer experience increased, we were able to make better looking apps quicker, with fewer bugs.
The (at least slightly) Good
Being able to produce an iOS and Android App with one codebase
Using React Native you can produce both an iOS and Android with the same code. Those apps probably won’t look as good, are going to be more difficult to update and make changes to in the future, but you can develop apps for both platforms, and I guess that’s something.
Making really basic apps
React Native is good if you want to learn how to make a really basic checklist app. There are probably thousands of tutorials online. Maybe you’ll get lucky and find one that’s up to date.
People new to mobile development get through that tutorial and go “Look, I made an app, and it works on Android and iOS.” Did you mate? Try adding “make a more complicated app” to your todo list and see how far you get.
Good luck, you’ll have to figure that out on your own.
Hot Reload
Hot Reload is a feature that allows you to make changes to the code while the app is running, and see those changes instantly without needing to recompile the app, which might usually take between about 5 and 30 seconds depending on the amount of changes. This is particularly useful when building user interfaces allowing you to quickly experiment with the user interface and iterate fast, not having to wait for code to compile.
The thing is, it wasn’t reliable. Sometimes you would make a simple change like changing the colour of a button. You make the code change and you expect to see this reflected in the UI. But for whatever reason it didn’t. So you check the code for a typo. Nope. Maybe you’re supposed to specify the hex code for the colour rather than the name. Nope. So you eventually just try refreshing the app and it turns out the initial code change you made was correct, the UI didn’t update for some reason. I got to the stage where it was easier to reload or rebuild the app each time I wanted to try out a change because I was spending more time debugging why hot reload wasn’t working than the feature itself would ever save me.
As I found with many other problems I ran into in React Native, there was almost always never just one tried and true fix for anything which was really frustrating.
Adding to this, native development frameworks now have their own UI design tools in SwiftUI on iOS and Jetpack Compose on Android that have functionality similar to React Natives hot reload, so I don’t think hot reload can even be considered a benefit over native development these days.
The Bad
The theoretical benefits never actualised
-
- Development speed
- It’s easy to assume that since you’re using one framework to develop for two platforms, you might assume that using React Native you can develop an app twice as fast. In the smallest, best cases this might be true for a singular piece of functionality. But you still have to test both platforms. There were frequently issues on at least one of the platforms, or a specific version of Android and iOS which you’d have to make platform specific adjustments for.
- Development speed
I always felt really frustrated when I knew exactly how I’d accomplish something in natively in iOS and it felt like it would be quicker to learn Android development, and implement the feature natively on both platforms.
- Productivity
- Tracking down the source of the problem becomes more difficult. Is it a problem with the native iOS or Android platform, is it a problem with React Native itself, or a third party component?
- Single Codebase
- Somewhat true, but exceptions always need to be made whether that’s making UI adjustments or using different third party libraries for each platform.
- Smaller Team / Wider Talent Pool
- With React Native being able to generate both iOS and Android apps, you might think “Great, I can just hire a React Native developer rather than hiring an iOS developer and an Android developer”. The problem with this is, React Native doesn’t remove the need to have an understanding of Android and iOS, you’re just adding to the required knowledge needed to develop a mobile app. Ellen Shapiro summed it up well in this talk.
- Cost
- You can probably get a really bad React Native app done cheaper than a native app. Anything else is debatable.
- Can you afford to compromise on your users experience?
- Can you afford to miss out on new native functionality that isn’t available in React Native?
- Can you afford to delay the release of our project or a new feature because things are taking longer than expected?
- Can you afford to rewrite the app when React Native is no longer considered a viable platform?
- You can probably get a really bad React Native app done cheaper than a native app. Anything else is debatable.
Learning Resources + Documentation
I found it extremely difficult to find any good learning material. In addition to React Native’s own documentation, I purchased books and video courses, and I didn’t get much value out of any of them. Everything was aimed at beginners, which is fine for beginners, but when you want to create a serious app used by actual people and rather than one well tested and documented solution to solve a particular problem there’s many different ways that have varying levels of success, and it wasn’t clear what the best path was without trying them all out first. I think this is partly due to the fact that React Native is a less mature platform. With iOS and Android it’s a lot more clear what the best, tried and true solutions are to a lot of problems.
The Ugly
It’s much more difficult to create a good looking app
iOS and Android are different platforms. Apple have the Human Interface Guidelines and Google have Material Design, and they are both really good resources on learning how to make apps that look good on their individual platforms. These are not hard and fast rules of how your app needs to look, but it’s an excellent starting point and you can customise from there.
When you develop an app to look the same on every device, it might look consistent to you, but it looks out of place to your users. This article provides a number of great examples of differences between Android and iOS.
With React Native, some base components do adapt per platform, but a lot of things don’t, particularly when you get into using third party components. Quite often with third party libraries you would find components that copied the style of a native iOS component but then they just used the same thing for Android, because the developers either didn’t know any better or didn’t care.
Instability and cryptic errors
Facebook’s motto was “Move fast and break things” up until 2014, when they changed it to “Move fast with stable infrastructure”. Unfortunately it seems as if the React Native team didn’t get the memo, because there were a number of updates that broke everything.
Some React Native releases would break third party dependencies, so you had to wait for maintainers of those broken dependencies to approve changes before you could upgrade the version of React Native you were using. Even then it wasn’t a case of simply choosing to use a new version, you had to incrementally add or remove particular lines to particular files within your project which was different for each version, and very easy to make a mistake which resulted in entirely unhelpful error messages.
Reliance on Third Parties
React Native is developed by Facebook, and it’s no secret that Facebook maybe isn’t the most trustworthy company. While they’re not on Google’s level of killing products, there’s definitely potential for them to abandon React Native.
And on top of that, a lot of functionality that a developer would expect on native platforms is left up to the community.
For example, Navigation – the management of presenting and transitioning between different screens is a fundamental concept to mobile apps. Very rarely does an app consist of just a single screen. But React Native’s official documentation points you to two third party options, and there are dozens of other alternatives. In my experience they all had their own drawbacks which you don’t realise are an issue until you actually try to use it, because you just expect certain functionality to be there.
Then for any other component that React Native doesn’t provide, you either have to make it yourself, which involves doing the native work on both platforms, plus the work to bridge it to React Native, or you rely on a third party library. Using a third party library is commonplace in software development, but a particular issue I found with React Native libraries was that they were much more likely to just be abandoned by the developer, probably because the developer stopped using React Native.
For example in one of our projects, there were 42 React Native specific dependencies. 25 had not been touched in over two years. Compare that to a native iOS app with similar functionality which had 10 dependencies, and only one of them had not been updated in two years.
When third party libraries get abandoned by the developer, you’ve got a couple of options:
- Don’t upgrade the version of React Native you’re currently using. Not a good long term solution.
- Find a replacement. This takes time and effort to look for a suitable replacement, and make any necessary code changes to integrate it. And if you don’t find a suitable replacement…
- Build it yourself. Hopefully you’ve got some developers that are experienced in native iOS and Android.
The Community
A lot of the React Native community are people getting into mobile development for the first time. They don’t know what they’re doing, and they don’t know any better. They choose React Native because they think it’s the easiest way to build an iOS and Android app, and then they take advice from each other and end up with mediocre and buggy apps. To paraphrase a former coworker: “React Native allows dumbass programmers to do dumbass things”.
When I had to Google the problems I ran into (which was constantly), it was clear that a lot of people didn’t know what they were talking about.
And sometimes even the developers of third party modules were… less than helpful.
Yeah, I’m just gonna go get another computer because React Native doesn’t work on this one.
(Lack of) Attention to detail
Here’s a screenshot of an example from a form component I tried out. This is what the developer thought was a good demonstration of their form component:
Notice the different text sizes in different fields of the form. The vertical alignment of text in the forms. The thick separators. The way the fields are just cut off on the edges. Compare this to how forms look natively on iOS:
It’s much cleaner and more consistent. It’s that attention to detail that a lot of developers in the React Native community lack.
It’s not as popular as you might believe
With big names like Facebook, Microsoft, and Shopify all using React Native to some extent, it’s easy to think that React Native is a lot more popular than it really is. According to Appfigures, 2.6% of free iOS apps and 4.4% of free Android apps use React Native.
These figures include apps that only use React Native on one screen and are othewise natively written. For example the core experiences of Facebook, Messenger, and Instagram don’t use React Native because it doesn’t provide the best user experience.
In Summary
Native apps are not going away. And neither will the succession of cross platform frameworks that claim to be the ultimate solution to writing once and running everywhere. In 2013 people would tell you PhoneGap is the future of mobile development (it wasn’t). In 2015, they’d tell you Xamarin was the future (it wasn’t). In 2017, React Native was the new hot thing (you see where this is going), and now every person with an hour of Flutter experience and a Medium account wants to show you a Google Trends graph in an attempt to convince you that Flutter is going to be the future of mobile development (it’s not).
Since moving back to developing separate native apps for iOS and Android, app development has become a much more pleasant experience. We’ve been able to produce more functional and better looking apps in a shorter amount of time than we could ever have done with React Native or any other cross-platform framework.