What cross-platform development really shares
Cross-platform app development means writing some or all of an app once and running it on iOS and Android. How much is shared varies by approach. Flutter and React Native typically share both UI and logic. Kotlin Multiplatform usually shares business logic, networking and data layers while each platform keeps its own native UI, though Compose Multiplatform can now share UI as well.
Even with high code reuse, some work is always per platform: store listings, signing, push notification setup, permission flows, accessibility testing and platform-specific bugs. Budgets that assume a shared codebase halves the total cost tend to disappoint; savings of a meaningful but smaller share are more realistic.
Comparing cross-platform frameworks
| Criterion | Flutter | React Native | Kotlin Multiplatform |
|---|---|---|---|
| Language | Dart | TypeScript / JavaScript | Kotlin (plus Swift for iOS UI) |
| What is shared | UI and logic | UI and logic | Logic by default; UI optional |
| UI rendering | Own rendering engine | Native platform views | Native UI or Compose Multiplatform |
| Best team fit | New team or mobile-focused team | React web team | Existing Android/Kotlin team |
| Adding to an existing native app | Possible as a module | Possible, with more setup | Designed for incremental adoption |
| Web reuse | Flutter web for app-like UIs | Logic sharing with React web apps | Kotlin/JS or Wasm, less common |
For the deep dives, see our dedicated pages on React Native development and Flutter development services. .NET MAUI is another option for organizations committed to C# and the Microsoft ecosystem; we will discuss it if it fits your context.
How we choose an approach
We score the options against a short list of questions during discovery. The answers usually make the choice obvious, and writing them down protects the decision when new stakeholders ask why later.
- Which features depend on platform-specific APIs, and how central are they?
- Who will maintain the app in two years, and what languages do they know?
- Is there an existing native app, web app or backend whose code we should reuse?
- Does the design follow platform conventions or a custom brand system?
- Are there performance-sensitive areas such as real-time media, maps with heavy overlays or large offline datasets?
- Will desktop or web versions be needed from the same code?
Team composition often settles the question. A company with five React engineers and no mobile specialists will get further, faster, with React Native. A company with a strong Android team has a natural path through Kotlin Multiplatform. A new team built for the product can choose on technical merits alone, and Flutter's consistent rendering often wins when design control is the priority.
Kotlin Multiplatform: sharing logic while keeping native UI
Kotlin Multiplatform takes a different route from Flutter and React Native. Instead of replacing the native UI layer, it compiles shared Kotlin code to JVM bytecode for Android and to a native framework that Swift can call on iOS. Each app keeps its own interface, SwiftUI on iOS and Jetpack Compose on Android, while networking, persistence, validation and business rules are written once.
- Ktor for shared networking and kotlinx.serialization for JSON models
- SQLDelight or Room for a shared local database with generated type-safe queries
- Coroutines and Flow exposed to Swift through generated wrappers for async APIs
- expect/actual declarations for the few places where each platform needs its own implementation
This approach fits teams that value fully native interfaces, or that already have a mature Android app and want to reach iOS without starting over. The trade-off is that UI is still built twice, so the savings are smaller than with a fully shared UI. Compose Multiplatform narrows that gap for teams willing to share UI as well.
Mistakes we see in cross-platform projects
Cross-platform apps that struggle usually do so for organizational reasons more than technical ones. These are the patterns we look for when auditing an existing project, and the ones we design against on new builds.
- Testing only on the developer's own phone, so the other platform ships with untested flows
- Copying iOS navigation patterns onto Android, breaking the system back gesture users expect
- Pulling in a plugin for every small need, leaving dozens of dependencies to keep current
- Letting framework and SDK upgrades slide until a store deadline forces a rushed migration
- Treating native code as nobody's responsibility, so integration bugs sit unresolved
Our article on common mobile app development mistakes covers related planning errors that apply whichever framework you choose.
Architecture for a shared codebase
The main risk in cross-platform projects is platform conditionals creeping into shared code until no one can predict how a change behaves on each OS. We contain that with a few structural rules.
- Platform-specific behavior lives behind interfaces with one implementation per OS
- Shared domain and data layers have no dependency on UI or platform packages
- Design tokens and components live in one library used by every screen
- CI builds and tests both platforms on every change, so a fix on one cannot silently break the other
- Native code is owned by named engineers with Swift and Kotlin experience, not treated as an afterthought
The backend is the same regardless of framework, and it often decides more about long-term quality than the client does. We design APIs, authentication and notifications to serve mobile and web clients alike, which also simplifies adding a progressive web app later.
Testing and releasing on two platforms
Shared code reduces duplicate logic tests, but device testing still runs on both platforms. We maintain a device matrix for each OS, automate end-to-end tests for critical journeys, and run manual exploratory passes on platform-specific areas such as permissions, deep links and background behavior.
Releases go to TestFlight and the Google Play internal track at the same time. Store review is separate and unpredictable, so we plan launches with a buffer and use phased or staged rollouts on both stores.
We also keep release notes, version numbers and build numbers aligned across platforms, which makes support conversations far simpler. When a user reports a bug, support should be able to tell from one version string exactly which shared code and which native code they are running. Crash reporting is configured with symbol files for native code and source maps or debug symbols for shared code, so stack traces are readable on both platforms.
Cost considerations
Covering both platforms with one codebase is often the most cost-effective route for startups and SMBs. Typical US-market budgets are about $30k–$90k for an MVP and $60k–$150k for mid-complexity apps, with complex products above that. These figures vary with scope and integrations.
If you are still deciding on platform strategy, our native vs. cross-platform comparison and mobile app cost guide are useful companions to a first call.