Before I start this article, I'd like to ask you a question, a really small one: how does a native app feel to you?
Depending on who you are, this could mean a lot of different things. For some, this could mean the app having the platform's components or design language invented by the same company that created the platform: Material Design by Google for Android; Apple hasn't given theirs a name, at least not yet, but it is known in the developer docs as Human Interface Guidelines or HIG for short, for iOS, macOS, watchOS, tvOS and now visionOS (hopefully I haven't missed any OS's); Fluent UI by Microsoft for Windows. And then the application doing what it was built to do.
For some, this means the applications should work at the best possible performance, while having the beautiful standardised look from their own UI language and also obeying the platform behaviours users are already familiar with. Well, the above are all guidelines and not rules, so why not? 🤷♂️
The Cost
Every company loves a truly native application, but then come the thoughts:
Thoughts about the cost involved in hiring separate developer teams for each platform. Or training their current developer teams to pick up those platforms. So they choose something that helps them build for almost all platforms, for this article iOS and Android, with a single developer team, same codebase, less hiring, less time to ship, and easier standardisation of UI or targeting platform components. But the choice of framework for this agenda has great effects, sometimes not reversible. Two popular ones are React Native by Meta (formerly Facebook) and Flutter by Google. Now, on what fronts do they compare, since they solve the same problem?
React Native vs Flutter, The Internals
Now I'll try my best not to be partial here, but you'll know where I stand. First, let's take a look at how they work, from the inside.
React Native
First things first, you write your simple, most important React Native app in JavaScript or TypeScript. Then at build time, it's bundled together with its dependencies of packages you used, some in native code for those that access platform APIs and some, just JavaScript or TypeScript. This is put together with a JS-Native bridge so your bundled code can communicate with the platform and a VM to run the JS code, then Fabric to render the results to the user and also send actions to your code. This approach, while it makes a lot of sense, I see to be built for higher-end devices. A lot of lower-end devices struggle, especially on the side of rendering, from my few years working with React Native. The user interacts with your app, then it takes some time to get feedback for what they just did. Quite frustrating, if you ask me. But for high-end, well-optimised devices, it works.
Flutter
This framework takes on a different approach. I call it the game engine approach. Games have been known to be notoriously compatible with a lot of platforms with little to no adjustments, and much of this success is owed to 'Machine code'. The language that our devices (computers) all understand well. When you write your Dart code, at build time, your code is compiled to machine code together with your dependencies if Dart code, and then platform-specific packages, put together with the Flutter engine. When a user interacts with your app, the Flutter engine handles all the interactions and communicates with the machine code, just like games work. And since there is no bridge, this is fairly easy to handle for lower-end devices. And I can very much attest to this.
React Native vs Flutter, The Developer Experience
As we now know how they work on the inside till you ship to the user, let's look at how it feels to work with them. They are both good tools and a lot of work has been put into them by very smart engineers, which I appreciate very much, so this experience is simply pain points and happy points I have gathered from my work with them and then from developer friends that do work with them.
React Native
In the React Native world, well, development is quite simple and some will say very easy, but all you have to do is give your whole development workflow to one company, Expo. Handling everything by yourself is so hard, it feels like a sin. It also does not mean builds on Expo are all perfect, since they happen on the cloud and not controlled by you. They seem to take away some time out of your development time. If you are working with the Expo Go app while developing, you will have some good experience with running your app because it's fast to start up and see your changes, but then not all your dependencies will come bundled with the Expo Go app or even work from it. In that case, you have to take the development build path and wait for a development build to complete before seeing your app. I believe this doesn't have to happen often, usually the first time, unless you reset your Simulator/Emulator once in a while, like me, or you add a new native dependency.
It's worth noting that React Native isn't without its own quirks. While it borrows heavily from React for web, many paradigms work very differently in React Native. For instance, you'll find yourself nesting Flex components quite often for layouts, which can also lead to deeply nested structures. Also, things like styling, navigation, and even some basic components behave differently from what you might expect coming from web development. This learning curve can catch some developers off guard, especially those experienced with React for web.
Another thing is the whole workflow seems to run on a URL and depends very much on your network or internet to function, so if you work in a place with erratic internet, or need to switch networks, or worse of all, no internet, you will have a horrible time working with React Native.
On the bright side, React Native offers hot reloading, which is a lifesaver during development. It lets you see your changes almost instantly without losing the app's state. Plus, if you're already familiar with JavaScript and React, the learning curve isn't too steep.
When it comes to testing and debugging, React Native has a decent set of tools. You've got Jest for unit testing, Detox for end-to-end testing, and the React Native Debugger for, well, debugging. It's not always smooth sailing, but it gets the job done.
Flutter
On the Flutter side of the world, development experience is fairly easy too, but it's more of the managed workflow from React Native. You handle everything yourself but it's well documented. While there are companies that are doing the kind of awesome work Expo is doing like Invertase with their Globe product, Code Magic, for a simple build and deploy like Expo's EAS platform. But the development process does not rely much on your internet while developing, the build is run on your local machine, and then when ready is installed and launched on your connected device, simulator or emulator. The only URL you get is for the dev tool which uses web sockets to communicate with your app. I haven't had many pain points with Flutter on the side of developer experience though. It seems like a lot of work for some of my developer friends. Like you have to learn a whole new language, Dart, not very hard, and some don't like the widget tree kind of code layout.
A lot of my friends complain about the nested widgets approach in Flutter. It can lead to deeply nested code that some find hard to read and maintain. However, I've found that with proper formatting and breaking components into smaller parts, it's manageable and even intuitive once you get used to it.
Flutter also has hot reload, and I find it to be even faster than React Native's. It's a joy to use, especially when you're tweaking UI elements. As for testing and debugging, Flutter comes with a pretty comprehensive suite of tools right out of the box. You've got unit, widget, and integration testing built-in, and the Flutter DevTools are quite powerful for debugging.
The Ecosystem and Community
Both React Native and Flutter have active communities, but they're at different stages. React Native, being older, has a larger community and more third-party packages available. You can find a package for almost anything, which is great. However, the quality can be hit or miss, and keeping all these packages updated can be a bit of a headache.
Flutter's community is younger but growing fast. The packages available are generally of high quality, thanks to Google's efforts in maintaining standards. You might not find as many packages as in React Native, but the ones you do find are usually well-maintained.
UI Consistency and Native Performance
React Native uses the native components of each platform, which means your app will look and feel native on both iOS and Android. However, this can sometimes lead to inconsistencies between platforms, and you might need to write platform-specific code to get things just right.
Flutter, on the other hand, draws every pixel on the screen itself. This means you get pixel-perfect consistency across platforms, which is great for branding. But it also means that your app might not feel 100% native on either platform. That said, Flutter's performance is generally excellent, even on lower-end devices.
What do I pick? 😀
I am always okay with my friends picking any tool as long as they have gone through profiling, weighed in the benefits their users are getting and also their mental health. And I also love the work both the RN team and the Flutter team are putting into providing us with these awesome tools to save our companies money and time.
On the brighter side of things, there are a lot of React Native roles out there compared to Flutter. All the Telcos in Ghana use React Native. Some banks do. My company Pharst Care uses Flutter. And you will notice the performance difference, especially on lower-end devices where we have not seen a lot of performance issues. So I stand a lot with Flutter; it should be your top pick, you'll have a good experience. And yeah, I know you don't like learning another language but Dart is a fairly simple language to grasp if you understand the foundations from other languages.
When it comes to real-world examples, React Native boasts apps like Facebook, Instagram[errm, questionable], and Airbnb[errm maybe]. Flutter, while newer, has impressed with apps like Binance [yeah they rewrote], Google Ads, Alibaba's Xianyu, and ByteDance’s Tiktok. Both frameworks are capable of producing high-quality, performant apps.
Looking to the future, both frameworks seem here to stay. React Native has the backing of Meta and a huge community, while Flutter has Google's support and is gaining traction rapidly. They're both expanding beyond mobile too, with web and desktop support.
I also believe they are all tools, and you should know how both of them work and pick out the good and bad parts. The choice often comes down to your team's expertise, your project's specific needs, and your target audience. And that's it for this article. I'm Aikins — I love to build stuff.