Every mobile project starts the same conversation.
"We want iOS and Android. We want to share as much code as possible. React Native is probably the answer, right?"
Maybe. But I've built enough mobile projects in both approaches to tell you that "React Native is probably fine" is not a strategy. It's a way of deferring a decision until the decision makes itself—usually at the worst possible moment, usually in production.
What React Native Actually Is
React Native runs your JavaScript on a separate thread and communicates with native components via a bridge. For most UI interactions—scrolling, tapping, basic animations—this works well and the UX is indistinguishable from native.
The New Architecture (JSI, default from RN 0.73+) replaced the asynchronous bridge with direct synchronous C++ calls. This closed the performance gap substantially. The ceiling is higher than it was in 2020.
But "closer to native" is not "native." The performance ceiling still exists. The question is whether your app hits it.
When React Native Is the Right Choice
Standard CRUD applications. Internal tools. Content-heavy apps with simple navigation. Apps where business logic is complex but UI is not. MVPs where you need to validate product-market fit before investing in native.
A fintech dashboard that shows account balances, transaction history, and initiates transfers? Build it in React Native. A customer-facing banking app that needs biometric authentication, secure enclave storage, and complex gesture-driven interactions? That's a different conversation.
The honest math on code sharing: in a well-structured React Native project, you'll share 60-75% of your code between iOS and Android. The remainder—platform-specific navigation patterns, push notification handling, deep linking configurations, permissions flows—needs platform-specific implementation. Anyone who tells you 100% code sharing is realistic has never shipped a production mobile app.
When Native Wins
High-performance graphics and animations. Anything that requires 60fps animations on budget Android hardware. React Native can achieve this for simple animations. Complex, continuous, physics-based animation—the kind you see in premium fintech and fitness apps—is significantly harder. You can drop into native code via Fabric rendering, but at that point you're writing native code anyway.
Deep OS integration. Background location tracking, Bluetooth Low Energy peripherals, NFC payments, ARKit/ARCore. You can access all of these from React Native via native modules, but the integration complexity scales quickly. When your app's core functionality depends on BLE, writing a native module in Swift/Kotlin and calling it from JS adds overhead that native-first development doesn't have.
Camera and media processing. If your app's primary value proposition is in the camera—photo editing, video recording, real-time filters, computer vision—you want native. React Native camera libraries work adequately for basic use cases. For anything requiring fine-grained control over the capture pipeline, you'll be fighting the abstraction.
Security-critical applications. iOS Secure Enclave and Android Keystore provide hardware-backed cryptographic key storage. React Native libraries can access these, but the trust boundary is less clean. For apps where the key derivation path and key usage are audit requirements—regulated financial apps, healthcare, enterprise security—native gives you cleaner answers on the security review.
The Expo Question
Use Expo unless you have a specific reason not to. Expo Managed workflow eliminates a significant amount of React Native's configuration overhead. The development experience—hot reload, OTA updates via EAS Update, simplified build pipeline via EAS Build—is genuinely better than bare RN.
The exit path exists: you can eject to bare workflow when you need native modules that Expo doesn't support. Most projects never need to.
The only cases I don't reach for Expo: apps with unusual native module requirements on day one, or enterprise clients with specific build pipeline constraints.
Flutter Is a Real Option
Flutter is technically impressive and solves some of React Native's problems. Dart compiles to native ARM code. Flutter's rendering engine (Impeller) draws everything itself rather than delegating to native components. The result is more predictable performance and a higher performance ceiling for animation-heavy UIs.
The trade-offs: Dart is not a widely-known language, so team ramp-up is longer. The component library is more opinionated. Web support exists but isn't as mature as React Native's. If your team has strong TypeScript expertise, React Native has lower switching costs.
Flutter is worth evaluating seriously if: animation performance is your primary concern, you're building a highly custom UI that doesn't follow platform conventions, or you don't have an existing web team whose skills you want to leverage.
My Decision Framework
Before I recommend an approach, I want to know four things:
1. What are the performance-sensitive interactions in this app? If the answer is "we're not sure yet," that's an argument for React Native first—it's easier to replace parts with native later than to redo a native app.
2. Does the app require hardware features beyond camera, GPS, and accelerometer? BLE, NFC, ARKit, Secure Enclave—these push toward native.
3. What does your existing team know? React Native with an experienced web team shipping in 8 weeks beats a native app with an inexperienced team shipping in 20 weeks.
4. What's the performance target on the lowest-end device you need to support? Premium iOS is forgiving. Mid-tier Android is not.
The Practical Conclusion
React Native is the right default for most mobile projects. It ships faster, requires fewer specialised skills, and the performance gap with native has closed significantly with the New Architecture.
But "React Native is the default" doesn't mean "never consider native." It means start with React Native, know the performance ceiling, and make a deliberate decision to invest in native code for the specific features where it matters.
The projects that get this wrong are the ones that choose React Native without understanding the ceiling, hit it in production, and then spend three months writing native modules to paper over the gaps. That's more expensive than making the right call at the start.