React Native vs Flutter: what actually decides it for us
React Native versus Flutter comparisons usually turn into a feature checklist that doesn't actually help anyone decide. Both render native-feeling UI, both have mature ecosystems, both ship to iOS and Android from one codebase. The decision that actually matters for most clients comes down to three things: existing team skills, how much custom native functionality the app needs, and how much the UI needs to diverge from each platform's own conventions.
We've shipped production apps in both, across roughly forty mobile projects at this point, and the pattern holds: teams that pick based on a blog post's feature comparison table are usually less happy eighteen months later than teams that picked based on which framework actually fit their specific product and team.
If you already have a React web team
React Native shares enough with React that a web team can contribute to the mobile codebase without starting from zero, which matters a lot for a small team wearing multiple hats. Flutter's Dart language and widget model are a genuine context switch even for strong developers, and that ramp-up cost is real even though Dart itself isn't hard to learn.
We've seen this play out directly: a client with an existing Next.js web team picked React Native specifically so two of their web engineers could rotate onto mobile work during slower sprints. That flexibility doesn't exist with Flutter unless someone on the team already knows Dart, and hiring for Dart specifically is a smaller, more competitive pool than hiring for JavaScript.
If the app needs deep native integration
Both frameworks support native modules, but React Native's bridge architecture has historically made certain classes of native integration, background processing and some camera or Bluetooth work in particular, more finicky to get right. Flutter's more consistent rendering pipeline tends to handle heavy custom UI and animation work more predictably, which matters for apps where the interface itself is the product.
React Native's newer architecture, with its more direct native communication layer, has closed a lot of this gap over the past couple of years, but the ecosystem of third-party native modules built against the older bridge is still large, and quality varies a lot from package to package. We budget extra QA time specifically for any React Native project touching Bluetooth or background location, because that's consistently where the rough edges show up first.
Where Flutter tends to win outright
For apps where custom animation and pixel-precise UI matter more than platform-native look and feel, a fitness app with heavy custom charts, a design-forward consumer product with a distinctive visual identity, Flutter's rendering model gives more consistent results across both platforms without fighting each OS's own component library. Because Flutter draws every pixel itself rather than bridging to native components, what you see in design tools tends to match what ships far more reliably.
This same trait is a downside for apps that specifically want to feel native to each platform, since a Flutter app has to deliberately reimplement iOS and Android's distinct interaction patterns rather than getting them for free the way a more platform-native approach can.
Hiring and long-term maintenance costs
React Native's talent pool is larger simply because JavaScript and React are so widely known, which matters if the client plans to eventually build an internal team rather than staying on an agency retainer indefinitely. Flutter developers are a smaller pool but the framework's stricter conventions mean codebases tend to be more consistent across different engineers, which can actually reduce onboarding time for a new hire once they've learned Dart.
We factor this into the recommendation explicitly: a startup planning to hire its own mobile team within a year leans us toward React Native almost by default, purely on hiring pool size, all else being roughly equal.
The decision that saves you the most money later
The bigger factor is usually not the framework at all: it's whether the product will ever need a feature that genuinely requires native code either framework can't cleanly reach. If there's a real chance of that (deep AR functionality, specialized hardware access, certain background audio processing) we scope for a hybrid approach from day one rather than betting the whole app on cross-platform and hitting a wall eighteen months in.
For most MVP-stage products we build, that wall never comes, and the cross-platform choice saves real time and money. The mistake is picking cross-platform by default without ever asking the question, not picking it at all.
A concrete example from a recent build
A logistics client came to us wanting a driver-facing app with background GPS tracking, camera-based proof-of-delivery capture, and offline support for areas with poor signal. We recommended React Native given their existing web team, but flagged the background location tracking specifically as a risk area during scoping, and budgeted an extra week of native module work for it up front rather than discovering the problem mid-build. That week of buffer turned out to be exactly enough, and the app shipped on the original estimated date because the risk was priced in rather than ignored.
The offline support ended up being the more interesting engineering problem, and not the one anyone had flagged as risky at the outset. Drivers routinely lost signal for twenty or thirty minutes at a stretch in rural delivery zones, and the app needed to queue proof-of-delivery photos and status updates locally, then sync them in the right order once connectivity returned, without ever showing a driver a spinner that implied something had failed. We built that sync layer as a thin queue on top of React Native's standard storage APIs rather than reaching for a heavier offline-first framework, because the actual requirement was narrow: don't lose data, don't confuse the driver, and reconcile quietly in the background. It's a good example of how the framework choice up front rarely determines the hardest problem in the build; disciplined scoping of the actual failure modes does.
What we actually recommend by default
Absent a strong signal either way, we default to React Native for most client projects, mainly because of the hiring pool and the ability to share code and sometimes engineers with an existing web team. We switch to Flutter specifically when the product's visual identity is unusually demanding, or when a client already has Flutter expertise on staff. Neither framework is the objectively correct choice; the correct choice depends on specifics most comparison articles never ask about.
The worst outcomes we've seen came from neither framework directly, but from a client picking one, hiring around it, and then discovering eighteen months later that a required feature needed a level of custom native work the team never budgeted for or hired toward. Ask the native-integration question before the hiring question, not after; it's a lot cheaper to answer at the start of a project than in the middle of one.
Ecosystem and third-party library maturity
For anything that needs a well-supported SDK, whether that's payments, analytics, mapping or a specific hardware integration, React Native's ecosystem is still the deeper one, largely because it has been around longer and shares a language with a very large web community. Flutter's package ecosystem has closed most of that gap for mainstream needs, but we still hit occasional cases where the official SDK for a service exists for React Native and the Flutter equivalent is a community port a single maintainer updates when they have time.
This is worth checking concretely rather than in the abstract. Before committing either way we list the third-party services the app actually has to integrate with and check what the support story looks like for each one on both frameworks. That takes an afternoon and it has changed the recommendation on at least two projects.
For anything that needs a well-supported SDK, whether that's payments, analytics, mapping or a specific hardware integration, React Native's ecosystem is still the deeper one, largely because it has been around longer and shares a language with a very large web community.
Build times and iteration speed during development
Flutter's hot reload is genuinely faster and more reliable than React Native's fast refresh, and on a long build that difference compounds into real developer hours. It rarely decides the framework on its own, but on a project with a heavy UI iteration phase, meaning a lot of design review cycles and a lot of small visual adjustments, it is a real cost difference rather than a matter of taste.
App size and cold-start time
Flutter apps ship a rendering engine, so the baseline app size is larger. In practice both frameworks produce apps well within what users tolerate on a modern device and a decent connection, and the difference is not something we would let decide a project by itself.
It does matter in one specific case: if a meaningful share of the audience is on older Android hardware or downloading over a constrained connection, app size stops being an abstraction and starts affecting install completion rates. That is worth checking against the client's actual analytics rather than assuming.
Neither framework avoids native code entirely
Both are sold on the promise of one codebase, and both mostly deliver it, but every non-trivial app we have shipped on either framework has needed some amount of platform-specific work: a native module for a device capability, a permissions flow that differs between iOS and Android, a build configuration issue that only appears on one platform.
The realistic expectation is not zero native code but far less of it, and the practical question is whether the team has anyone who can write that native code when it becomes necessary. A team with no native experience at all will hit a wall on either framework eventually; it is worth knowing that going in rather than discovering it under deadline.
The case for going fully native anyway
Cross-platform is our default, not our religion. If the app's core value is something that lives close to the hardware, such as sustained camera processing, complex audio or heavy background location work, then two native codebases can be the cheaper path once the workarounds are accounted for.
This is a minority of projects, and the honest signal is when the list of things that need a native module starts looking like most of the app. At that point the cross-platform layer is overhead rather than leverage.
What changes if the app needs a web version too
If a web version is on the roadmap rather than hypothetical, React Native tilts further ahead. Sharing business logic, types, and often whole components between a React web app and a React Native mobile app is a real, everyday saving rather than a theoretical one. Flutter's web target works, but the output is a canvas-rendered application with the SEO and accessibility trade-offs that implies, which rules it out for anything that needs to be indexed or read by a screen reader in the normal way.
A framework choice we reversed mid-project
We have changed framework mid-build exactly once, and it is worth describing because the reason was not technical. We had recommended Flutter for a client with no existing mobile team, on the reasoning that the framework choice was a clean slate. Two months in, the client hired two developers, both with React backgrounds, both hired for reasons that had nothing to do with our project. The calculus inverted. The remaining build was cheaper to restart in React Native than to staff a Flutter codebase the client's own new team could not maintain.
The lesson we took was not about either framework. It was that the hiring question is a live variable rather than a fixed input, and on a project long enough for the client's team to change, the more important question is which framework the people who will own this in two years are likely to know.