Flutter is better than React Native*

*…in all the ways that don’t matter.

Jamon Holmgren
Red Shift

--

Hand holding a phone that has code in background and React Native vs Flutter. Background has code snippets with a cut apple overlapping to reference the phrases “Any way you slice it” and “comparing apples to apples”
Artwork by Jenna Fucci

Flutter and React Native are natural competitors as two of the most used multi-platform mobile app frameworks. Debates swirl…developers argue…and, in the end, apps are shipped, using one or the other.

So which is better?

The smart answer, the one that senior engineers will say with a cheeky grin, is: “It depends. Both have pros and cons, and in the end it’s a decision about tradeoffs.”

But that’s hardly a satisfying answer. It’s like saying the best player in the NBA is “depends on their role”. That doesn’t spur talk show hype, drive clicks, and increase engagement!

So let’s cause some drama and ruffle some feathers, and talk about why Flutter is better than React Native…in all the ways that don’t matter.

Billboard signs reading “Now Hiring”, “Dev Experience”, and “& More!”

What does matter?

We could talk about all the things that don’t really matter: performance, developer experience, Dart vs JavaScript, native integrations, standard libraries, and much, much more.

The title is provocative; I’ll give you that. But this article isn’t just click-bait! In fact, I am going to do a deeper dive into most of those things in a more nuanced way later in this article.

But let’s cut to the chase. What does matter?

The answer lies not in the technical aspects of React Native and Flutter at all. It is not even something that a mobile developer might care about.

But to the people making decisions on what to use for their next app, this is what matters right now:

Hiring

Hiring developers is famously difficult right now. Tech salaries are up and supply is limited. Finding qualified developers that want to work for your company is like pulling teeth.

Flutter developers are passionate and high quality — this is in no way throwing shade at the Flutter development community! They just aren’t … high quantity. It’s not like you have a gigantic pool of Dart developers to pull from. You’re always training, pretty much every hire.

React Native developers, on the other hand, have a vast pool to draw from, and that’s the JavaScript community. JavaScript (and its popular variant, TypeScript) is by far the most popular programming language in the world and still growing quickly. React.js, which powers React Native, is arguably one of the biggest (if not the biggest) coding framework in the world. It’s still difficult to hire, but the pool of developers to draw from is far larger than Flutter’s. Taking a React developer and making them into a reasonably productive React Native developer is a straightforward process.

Sharing Code, Knowledge, and Developers

While hiring is the biggest, most impactful part of the decision to use Flutter or React Native, there’s another very important one I should mention, and that is sharing code, knowledge, and developers.

What’s better than good code? Less code. And one of the best ways to have less code is to share code between multiple apps. It can reduce your initial development time and simplify maintenance long-term.

Chances are, your company is using React.js, or at least JavaScript, in its websites, web apps, servers, and more. Being able to share code between your React.js app, Node servers, and more is a huge superpower for React Native — and something that Flutter isn’t nearly as strong on.

Beyond sharing code, it’s also a massive advantage to be able to share knowledge between your web, backend, iOS, and Android teams. You can have common wikis, Slack channels, and more. And there are a ton of tutorials, blog articles, StackOverflow Q&A, and more about React Native, React, and JavaScript online.

You can also, of course, share actual developers between these projects, and be able to ramp up headcount on a critical project without having to re-train nearly as much.

In the end, it’s hard to see past these absolutely critical factors when making a choice between React Native and Flutter.

Two laptops with code interfaces and a connecting line to show a connected experience

What about everything else?

But…what about performance? What about developer experience? What about accessibility, third-party library ecosystem, and more?

In pretty much every case, Flutter and React Native are neck-and-neck. Flutter wins some battles, React Native wins others. In no specific technical area (other than sharing code with web, as described above) does one framework win conclusively.

That’s it. That’s the article. You can stop. You got the point.

Okay … why are you still reading?

What’s that? You want specifics? Details?

Gant Laborde tells me, when you stick the landing, stop running. When you make the sale, stop selling.

But I can’t help myself. I love talking shop.

Let’s dive in deeper.

Developer Experience

Flutter’s team (and Google in general) are pretty good at developer experience.

Setting up your environment for Flutter development is generally easier than React Native. Flutter’s hot reload usually works better than React Native’s fast refresh. They have some really nice tools for debugging, profiling, and inspecting your widget tree. Their built-in end-to-end testing is a lot better than React Native’s Detox. Flutter’s CLI is first-class — I really love their flutter doctor command, for example, and you can manage your simulators and emulators right from the CLI.

Mike’s tweet

Flutter has a better upgrading experience as well, as you can just run flutter create in an existing app and it’ll recreate everything in the new version.

React Native’s tooling, on the other hand, has lacked a lot of the polish that Flutter’s tools have. React Native’s upgrading experience is one of the more painful aspects, as anyone who has worked with it for the past few years will attest.

It’s improving somewhat; in a recent React Native Radio episode (RNR 227), we talked with a couple of developers from Microsoft about the work they’re doing to improve React Native’s tooling and overall developer experience.

Additionally, Expo is designed to improve the developer experience tremendously in React Native, and with Expo Application Services you can now do just about anything in Expo that you could in vanilla React Native. Expo’s upgrading experience is excellent and their integrated tooling works very well. And if you’re not using custom native code, the Expo Go app is an amazing way to quickly share builds with others without compiling. Expo is a nontrivial advantage — if you’re using it!

TL;DR: Flutter’s developer experience is one of its strongest claims. React Native is catching up and has a ways to go — but Expo gives it a strong boost.

Performance

It’s difficult to compare performance in any software framework, much less something as complex as Flutter and React Native. Flutter and React Native are both “fast enough” for most situations, especially when a performance optimization pass has been done on them by competent developers.

Historically, though, Flutter’s out-of-the-box performance has been better than React Native’s. With that said, you can certainly write poorly performing Flutter apps along with very well-performing React Native apps.

The performance differences are mostly due to the asynchronous React Native bridge which will be replaced by concurrent communication between native and JS as the new architecture rolls out this spring. Additionally, the Hermes JS engine improves performance in a lot of key metrics. And lastly, Skia is now coming to React Native, which means you can have the same renderer for React Native that you would use in Flutter — but only where you need that sort of fluid canvas performance.

TL;DR: Flutter slightly wins (for now) on performance, but look for that gap to close very soon with the new React Native architecture.

Unified UI Experience

Flutter renders its UI using Skia and unifies its look and feel across all platforms. This lets them optimize the performance and do custom UIs without being constrained by the platform’s primitives.

React Native, on the other hand, uses UIKit on iOS, Android’s layout system on Android, and the DOM on web. This means that while you can build very similar looking apps, they will be flavored by each platform’s interpretation.

One criticism of Flutter is that it has to reinvent all kinds of things the platforms have already mostly solved, like accessibility, font scaling, and more. To be fair, they’ve done a fairly good job of this (by using lower-level built-in platform hooks), but this is yet another thing they have to re-implement that React Native can more or less rely on the platform primitives to provide.

It’s worth noting that Google developers have said that a unified experience is no longer a core goal. This doesn’t seem to match very well with Flutter’s approach.

TL;DR: React Native wins on this one if you accept the premise that it’s more important to match users’ expectations on a platform than have a unified experience.

Native Integrations

Integrating with the native platform is one of my favorite things to do on live streams (quick plug — check out my Twitch stream and click Follow over there!).

Flutter compiles the Dart code to native and has a pretty cool integration model with native code using what they call Platform Channels. It allows synchronous native calls and lets you write in Swift and Kotlin. Flutter’s documentation is really good here. They give you all the tools you need to test and mock these right out of the box. You get a lot of templates for each platform, and have first-class support for threading via Isolates.

In React Native, there is a bit of a learning curve to integrating with native. (That’s one reason I built React Native Colo Loco — to make that experience nicer!) Not to mention having to contend with the limitations of the React Native bridge. The documentation for native integrations is less than stellar as well.

It’s worth noting that the new React Native architecture is going to replace the bridge and bring all the advantages of native synchronous integrations to React Native, which does alleviate some of these deficiencies.

TL;DR: Both platforms have full native integration capabilities, but Flutter’s native integration tools are better.

Internationalization

Internationalization/localization (i18n) absolutely matters, and Flutter comes with built-in i18n support rather than relying on third-party support. However, React Native’s third-party i18n support is getting pretty good these days. Mazen Chami and I did a live stream recently implementing RTL language support (Arabic) into an app.

TL;DR: No advantage — both platforms do a pretty good job of internationalization, although some limitations still exist.

Built-In Navigation (& more)

Flutter is designed to be a bit more “batteries-included” than React Native, and one of the ways this is most obvious is that it comes with its own navigation/router solution.

Navigation is a particularly well-suited module for integration into the core framework, because it’s just so critical for most application. Imagine Next.js without its own router, for example. Just..not as useful.

React Native is much less opinionated and allows you to bring your own navigation solution. There are many out there, but the most well-supported are the (unfortunately named) React Navigation and React Native Navigation libraries.

Flutter also comes with built-in theming support and more. Ignite, our very popular React Native boilerplate, comes with its own theming support, but theming isn’t built-in to React Native.

The benefit of having built-in solutions like Flutter’s is that they can be tuned specifically to work well with each release of the framework. The downside of having built-in solutions like Flutter’s is when a better paradigm comes along, it can take an act of God to get the officially supported solution swapped out for the newer, better one.

TL;DR: Advantage to Flutter. Having a built-in navigator is nice, but React Native’s community provides some very nice options.

Web Support

Flutter 2 announced web and other platform support (Robin Heinze and I reviewed it on React Native Radio).

But their approach to web is very much “let’s use a canvas and draw on it” rather than using the native DOM. Sebastien Lorber went through that in more detail in this Twitter thread.

This comes with accessibility and SEO issues. It also requires that … well, you use Flutter for web, which is not really everyone’s first choice, to put it mildly.

twitter link

It’s worth noting that Flutter does offer an HTML/CSS/DOM version. It’s less used than the canvas renderer, and even with that, it doesn’t hold a candle to where React.js is at this point.

React Native, on the other hand, can share code directly whether you’re using React Native for Web or React.js directly. You can share a lot of business logic, data models, and more using JavaScript/TypeScript shared services and modules, while either splitting up the UI components or sharing them directly using React Native for Web. React.js was designed specifically for web and thus web is a first-class citizen, unlike Flutter for Web.

If you want a critique of Flutter, including its web support, check out Theo’s recent YouTube video on the subject and Luke Pighetti’s reaction to Theo’s video, where he disagrees with a lot of Theo’s conclusions about Flutter mobile but generally agrees about Flutter for Web.

TL;DR: Clear advantage to React Native. While Flutter 2 is making some moves that direction, React Native has a massive lead in web that it seems unlikely to relinquish.

Third-Party Libraries

In a typical React Native app, we use a number of libraries and tools that were originally designed for JavaScript or React. Some of those include axios, mobx, redux, lodash, ramda, eslint, babel, jest, prettier, react-devtools, typescript, npm, and yarn.

These are all libraries that web and Node developers use constantly. This means the combined communities can contribute and improve these tools, as well as (again) share knowledge and help each other.

Flutter, on the other hand, generally uses made-for-Flutter libraries. Some third-party Dart libraries are available, but that community is much smaller than JavaScript.

With that said, it’s worth noting that Dart comes with a unified formatter, testing, compiler, analyzer/linter, and package manager and is a type-safe and null-safe language. This means you would probably need fewer third-party libraries when using Flutter and Dart.

TL;DR: Flutter and Dart have some high-quality built-in tools, but the third-party ecosystem is a React Native advantage, as it’s almost impossible to replicate a robust ecosystem like JavaScript/React in an isolated community like Dart/Flutter.

Companies Using React Native vs. Flutter

React Native is truly a multi-corporation effort now. Besides Meta/Facebook, Microsoft is investing very heavily into React Native’s development. The core team collaborates constantly with Microsoft developers on various aspects of React Native. They’re rewriting many apps at Microsoft in React Native and building a ton of tooling and libraries for it. In fact, they even recently announced that the main Settings app in Windows is (partially) written in React Native!

In addition to Meta (Facebook and Instagram) and Microsoft, React Native is also used extensively by large companies like Coinbase, Shopify, Mercari, Discord, Pinterest, Tesla, Walmart, Wix, Salesforce, the NFL, MLS, Uber Eats, and many more.

Flutter is primarily supported by Google, which has a mixed record (at best) of long-term support of their products. There are some other companies using Flutter, including Toyota, eBay, and Alibaba, but most of the development is driven forward by Google.

With that said, Flutter has done a good job of doing their development in the open, fully open-source and driven by continuous community engagement and feedback — which is different from the general feeling that React Native, where it’s driven by what Meta/Facebook needs first, and done privately before eventually it becomes available externally. React Native’s core team has been working very hard to make the process more community-driven lately.

TL;DR: Advantage to React Native, with some additional nuance that can’t be captured in one sentence.

Dynamic Updates (Code Push, etc)

This doesn’t apply to every project, but I’ve heard from many companies that went with React Native because of the ability to do dynamic updates without having to submit to the App Store and Play Store and go through the approval process. This is not on the roadmap for Flutter.

TL;DR: Advantage to React Native.

Two buttons reading “yes” and “no” with a cursor hovering over both

So, should you use React Native over Flutter, then?

Well…it depends. Both have pros and cons, and in the end it’s a decision about tradeoffs. <smirk>

Despite the thousands of words that have been spilled on this subject, even the differences mentioned here are not really all that gigantic or obvious.

As I mentioned, having a vastly larger developer pool to hire from is the primary consideration. React Native is especially compelling if you already have web and backend developers using JavaScript/TypeScript and of course especially if you’re already using React.

If you are a Java or Android shop (Dart is easier if you come from Java/Kotlin) and/or need more of a unified, fluid UI, Flutter is certainly a viable option. It may be harder to hire for, but Flutter’s superior developer experience and performance are pretty nice consolation prizes.

In the end, the “it depends” cop-out really is the right answer. But — at least now you have more information to know what tradeoffs you’re making!

Other Thoughts

Any article that makes such a controversial claim will inevitably be scrutinized closely. This is my perspective, and given that I own a React Native consultancy and work with the React Native core team, I won’t claim to be unbiased. I have, however, done quite a bit of research, have worked with many companies considering both, and have run this article by several Flutter developers and gotten their feedback. They may not agree with my opinions and conclusions, but their feedback was definitely considered and implemented where appropriate. I want to be as fair as I can.

I also have tried not to focus on aesthetic things, such as Dart vs TypeScript’s syntax, or JSX vs Dart’s widget hierarchy. We could argue all day about which is better but those are purely personal preference and don’t impact the frameworks’ usability in any real way. Not that there aren’t differences between Dart and JavaScript/TypeScript — there are — but it would probably require another 2,000 word article to tackle that particular set of tradeoffs, and that’s beyond the scope of what I’m doing here.

Lastly, if you want to read a well-reasoned and researched counter-perspective, Isaac Lyman’s article on the Stackoverflow blog is worth checking out!

Acknowledgements

I’m indebted to several people for giving me feedback, helping me battle-test my assumptions, and sharing their own perspectives. Not all of them wanted their names printed here, but thank you nonetheless.

Thanks to:

  • My business partner Gant Laborde for his awesome advice and editing, as always — even though I ignored some of his best advice, at my own peril
  • Mike Diarmid for some awesome feedback, especially on the native integrations section and developer experience section
  • JD for sharing his experience comparing RN and Flutter 2 for several months at his company
  • Theo from Ping Labs for his incredibly hard-hitting video about this subject
  • Luke Pighetti for his well-reasoned reaction to Theo’s video and feedback on the article
  • Robin Heinze for her insight and proofing
  • Andrew Gabriel for his extremely helpful feedback
  • Derek Greenberg for his proofing and suggestions and encouragement
  • Jenna Fucci for her amazing custom art throughout this article
  • Also thanks to: everyone who “liked” my original tweet and forced me to write this. All 600+ of you jerks. ;-)

Have feedback? Hit me up on Twitter: https://twitter.com/jamonholmgren

This post has been translated into Korean.

--

--

Co-founder & CTO @infinite_red. Lutheran, husband, dad to 4, React Native Radio podcast host, Twitch streamer, hockey goalie. Talking shop!