KMP Shares Logic, React Native Shares UI — and That Changes Everything
The choice between KMP and RN is really a choice about which complexity you centralize and which you leave to platform specialists. Picking the wrong boundary for your team's structure and existing codebase turns a code-sharing win into a maintenance drag.
Kotlin Multiplatform and React Native represent two fundamentally different bets on where to share code. KMP leaves Android and iOS UIs alone — Compose, SwiftUI, XML — and moves domain logic, data access, and networking into a `commonMain` module that exposes UseCases, Repositories, and models. RN, by contrast, makes the UI the shared layer, with JS/TS components driving rendering on both platforms and native modules handling device capabilities.
This difference reshapes team structure and migration cost. KMP fits shops where Android and iOS engineers already own their platform UIs and want to eliminate duplicate business logic without rewriting screens. The shared module never imports `Context`, `ViewModel`, or SwiftUI; it returns `Result` or domain errors and lets each platform decide how to display a toast. RN suits teams that want one codebase for the interface and can maintain the JS-to-native bridge long-term.
Neither approach is free. KMP demands clean boundaries — code that reads `Build.VERSION` or passes `Context` must be refactored out — and iOS still carries the engineering burden of consuming Kotlin/Native frameworks, coroutines, and Flow from Swift. RN carries its own upgrade and compatibility tax. For a product with existing native clients and divergent UIs, sharing the business layer first is often the lower-risk move.
The article frames KMP and RN not as competitors in the same category but as solutions to different problems — one shrinks business-logic duplication, the other shrinks UI duplication — and the real decision is which layer your team can afford to share.
KMP's insistence on keeping UI on the platform side means it slots into existing native projects with less disruption than RN, which demands a more fundamental renegotiation of who owns the screen.
The migration advice is unusually pragmatic: start by moving pure Kotlin code and let compilation errors surface the unclean boundaries, rather than attempting a big-bang refactor.