What cross-platform actually delivers today
The argument against React Native and Flutter in 2020 was performance. JavaScript bridge overhead in React Native was real. Flutter was newer and less proven. Both arguments have aged poorly. React Native's new architecture — JSI and the Fabric renderer — eliminates the bridge entirely. Flutter's Skia-based rendering pipeline has been running production apps at 60fps on mid-range Android hardware for several years. For the large majority of app types, neither framework is a performance bottleneck.
The more relevant argument for cross-platform today is time-to-market and code reuse. A two-person mobile team maintaining a single React Native codebase ships features faster than the same team maintaining separate iOS and Android codebases. The productivity gap compounds over time: every feature added to one native app must be replicated in the other, while a cross-platform team writes it once. For most product categories — utilities, content apps, e-commerce, internal tools, B2B dashboards — this is the dominant consideration.
Where native still wins
Performance-critical UI remains the clearest native advantage. Anything that requires custom rendering at 120fps — fluid list animations, complex gesture recognisers, real-time data visualisation, games — is easier to build correctly in native than in either cross-platform framework. The frameworks can do it, but you will eventually be writing native modules anyway, which narrows the code-reuse advantage.
Deep OS integration is the second native advantage. Camera pipelines with custom processing (think document scanners, AR features, medical imaging), Bluetooth device communication, background task scheduling with specific timing guarantees, widget extensions, and app clips or App Intents in iOS all require tight OS integration that cross-platform frameworks abstract imperfectly. React Native and Flutter expose bridge APIs to the native layer, but each integration is an additional maintenance surface and a source of upgrade-related breakage.
Sensor and hardware access is the third. If your application depends on precise sensor data — accelerometer at high polling rates, GPS with raw NMEA data, barometric pressure for health features — the abstraction layers in cross-platform frameworks add uncertainty that native removes. Not always a deal-breaker, but worth testing against your specific requirements before committing to a framework.
How to evaluate your specific requirements
Start with the user interaction model. If your app is primarily forms, lists, navigation, content display, and standard UI controls, cross-platform will serve you well. If it has custom gesture-driven interfaces, complex animations tied to scroll physics, or real-time rendering, prototype the specific interactions in both options before deciding — do not assume.
Then inventory your OS integration requirements. List every platform feature your app needs: camera, notifications, location, background processing, biometrics, deep links, widgets, in-app purchase. For each one, check whether the cross-platform framework provides a maintained, well-documented abstraction. The ones that do not have a first-party or well-maintained community module are the ones that will slow you down or require native module development, which partially negates the cross-platform advantage.
Total cost of ownership and the hybrid approach
The total cost of ownership comparison is not just build cost. Factor in ongoing feature velocity, the cost of maintaining two native codebases versus one cross-platform codebase, the depth of the available talent pool for each option, and the cost of upgrade cycles when major OS versions ship. React Native and Flutter both require periodic migration work when the underlying platforms change; native development requires the same work, twice.
The hybrid approach is underused and often the right answer for complex apps. Build the core application in React Native or Flutter, and implement the specific features that require deep native access as native modules with a clean interface to the cross-platform layer. This gives you code reuse for 80% of the application and full native capability for the 20% that requires it. It is more complex to architect than a pure-play choice, but it is more honest about the tradeoffs and avoids forcing a binary decision where none is required.
