
Kotlin Multiplatform vs Compose Multiplatform: Understanding the Key Differences
Kotlin Multiplatform vs Compose Multiplatform: What Actually Differs and When to Use Each
You decided to ship iOS and Android from one Kotlin codebase. You opened a tutorial that mentions "Kotlin Multiplatform." You opened another and saw "Compose Multiplatform." A third used both terms interchangeably, sometimes in the same paragraph. The kotlin multiplatform vs compose multiplatform confusion is not your fault — the marketing collapses a distinction that the architecture demands you understand.
Here is the thesis in one line: Kotlin Multiplatform shares non-UI code. Compose Multiplatform shares UI code. They sit at different layers of your app. Most production teams in 2024–2025 use both. Some use only KMP. Almost nobody treats them as competing choices, because they aren't.
The confusion is suddenly urgent because KMP reached stable status in Kotlin 1.9.20 (October 2023) after roughly five years in beta, according to JetBrains' stability announcement. That changed KMP from "interesting experiment" to "real architectural choice." If you're picking a cross-platform stack now, you have to know which tool solves which problem — and which decisions you make independently.

Table of Contents
- What "KMP vs Compose Multiplatform" Actually Means
- What Kotlin Multiplatform Actually Shares
- What Compose Multiplatform Actually Renders
- Using KMP and Compose Multiplatform Together
- Choosing Your Setup Based on Team and Stage
- Five Production Patterns for KMP and Compose Multiplatform
What "KMP vs Compose Multiplatform" Actually Means
The kotlin multiplatform vs compose multiplatform comparison is structurally different from most "X vs Y" technology articles you've read. Those compare competing tools. This one compares two tools that operate at different layers of the same stack. Framing them as alternatives is the source of nearly every confused decision teams make about cross-platform architecture.
Start with the layered model. Kotlin Multiplatform is a language and compiler feature. It lets a single Kotlin codebase compile to JVM bytecode for Android, native binaries via LLVM for iOS/macOS/Windows/Linux, and JavaScript or WASM for the web. The Kotlin documentation describes this as the platform layer: you write Kotlin in commonMain and the compiler emits a binary appropriate to each target.
Compose Multiplatform is a UI framework. JetBrains built it on top of Kotlin Multiplatform. It reuses the same compiler plugin and runtime as Jetpack Compose, and exposes the same @Composable, remember, Modifier, and animation APIs. According to JetBrains' documentation on the relationship between Compose Multiplatform and Jetpack Compose, the only thing that changes between them is the target platforms — Compose Multiplatform adds iOS, desktop, and web; Jetpack Compose stays Android-only.
So you have three valid configurations:
- KMP alone — shared logic in Kotlin, native UI in SwiftUI on iOS and Jetpack Compose (or Views) on Android.
- KMP + Compose Multiplatform — shared logic and shared UI on both platforms.
- Compose Multiplatform alone without shared KMP logic — technically possible on Android-only setups, but rarely the discussion you're having.
Why do people conflate the two? Because both come from JetBrains. Both ship via the same Gradle plugins. Both target multiple platforms. The canonical JetBrains template bundles them together. The marketing presents them as one product; the architecture treats them as separable concerns.
Here is the mental model the rest of this article uses:
- KMP answers: How much non-UI code can I share? In practice, 60–80% of an app, according to Touchlab's KaMP Kit guidance based on production case studies.
- Compose Multiplatform answers: How much UI code can I share? Anywhere from 0% to nearly 100%, depending on how much platform-native polish you need.
These are independent questions. You answer them separately. The fact that JetBrains ships both together is a convenience, not a coupling.
Kotlin Multiplatform handles your app's brain. Compose Multiplatform handles its face. Calling them alternatives is like asking whether you need a CPU or a monitor.
What Kotlin Multiplatform Actually Shares
The compilation model first. KMP code lives in commonMain and compiles to each platform's native format — JVM bytecode for Android, LLVM-compiled native binaries for iOS, JavaScript or WASM for web. Platform-specific code lives in androidMain, iosMain, and so on, using the expect/actual pattern to declare an interface in common code and implement it natively per target. Google's Android team explicitly states that KMP code runs "on par with native implementations." That performance claim matters because it removes the "but will it be fast enough?" objection that haunted earlier cross-platform tools.
Here is what gets shared, what doesn't, and why:
- Business logic and state management — fully shareable. Use cases, domain models, validation rules, app state, and ViewModels (via
expect/actualor libraries like Decompose) all live incommonMain. This is the highest-ROI sharing because business rules almost never diverge between iOS and Android. - Networking — fully shareable via Ktor. Ktor's HTTP client plus kotlinx.serialization handle REST and GraphQL requests identically on both platforms. No platform-specific code is needed for a typical API integration.
- Local persistence — fully shareable schema, platform driver. SQLDelight defines your schema once in
commonMain; only the database driver (AndroidSqliteDriveron Android,NativeSqliteDriveron iOS) differs. Your queries run on both platforms unchanged. - Platform integrations — interface in common, implementation native. Push notifications, in-app purchases, camera, biometrics, and secure storage use the
expect/actualpattern. You declare the interface incommonMain, then implement natively inandroidMainandiosMain. The native code is usually thin — a few dozen lines per integration, not hundreds. - UI — not shared by default. Without Compose Multiplatform, KMP leaves UI native. That's deliberate. The 60–80% sharing figure comes from logic, not pixels.
| Capability | Shared via KMP | Platform-Specific Layer | Library/Tool |
|---|---|---|---|
| Business logic & domain | ✓ Full | None | Pure Kotlin in commonMain |
| HTTP/REST/GraphQL | ✓ Full | None | Ktor + kotlinx.serialization |
| JSON parsing | ✓ Full | None | kotlinx.serialization |
| Local database | ✓ Schema + queries | Driver only | SQLDelight |
| Dependency injection | ✓ Full | None | Koin |
| Image loading | ✓ Full | None | Coil 3 |
| Authentication logic | ✓ Logic | Credential storage native | Firebase Auth + Keychain/Keystore |
| In-app purchases | Partial | Store SDK native | RevenueCat |
| Push notifications | ✓ Routing logic | FCM/APNS delivery native | Firebase Messaging |
| Deep linking | ✓ Parsing | OS handlers native | Custom + platform intents |
| UI rendering | ✗ Without Compose MP | Full | SwiftUI / Jetpack Compose |
This table answers the practical question most teams open the docs to answer: what do I actually have to write twice? The honest answer is: not much, if you treat platform integrations as interface-only contracts. The 60–80% sharing figure validated across production KMP apps reflects this split. Almost everything below the UI layer is shareable, and the platform-specific code that remains is thin adapter code — not duplicated business logic.
Production starter kits like KMP Kit pre-wire these integrations (auth, payments, push, networking, database, DI) so you don't rebuild the bridge layer from scratch. The reported 122+ hours saved reflects the time that would otherwise go into wiring Ktor, SQLDelight, RevenueCat, Firebase, and Koin into a working Gradle structure with iOS framework export, source set targets, and CI/CD configured correctly. None of that work is interesting. All of it is required.
KMP does not force you to share everything. It gives you the architecture to share what makes sense — usually 60 to 80 percent of your codebase if you structure it right.

What Compose Multiplatform Actually Renders
Now the technical reality that drives every Compose Multiplatform tradeoff: Compose Multiplatform does not use native UIKit on iOS or AppKit on macOS. It uses Skia — a 2D graphics library — to draw every pixel itself. The JetBrains Compose Multiplatform overview describes this rendering model openly. On Android, it uses Jetpack Compose's standard pipeline (also Skia-based, but integrated with the Android view system). On web, it targets JavaScript and HTML, with increasing WASM support.
What this means concretely:
A Button in Compose Multiplatform on iOS is not a UIButton. It's a Skia-drawn rectangle that mimics Material or Cupertino styling. Accessibility, gestures, and system-level behaviors — context menus, share sheets, drag handles, system text-selection menus — must be re-implemented or bridged through the framework. iOS users notice when scrolling physics, keyboard handling, or text selection feels even slightly off, because those interactions are deeply muscle-memorized.
Maturity varies sharply by platform. According to the JetBrains compose-multiplatform release history:
- Android — stable. It's Jetpack Compose underneath.
- Desktop (Windows/macOS/Linux JVM) — stable.
- iOS — reached stable in 2024 after a long alpha/beta period. Production-ready for many use cases, still maturing for complex consumer UIs with heavy gesture and animation requirements.
- Web (JS/WASM) — alpha and experimental, improving fast but not yet a default choice for production.
When Compose Multiplatform Is the Right Call
- Apps where cross-platform UI consistency is the product: B2B dashboards, design tools, internal tooling, configurators.
- Apps with a strong custom design system that doesn't need to mimic platform defaults.
- Teams with deep Kotlin/Compose expertise and limited iOS-native bandwidth.
- Apps with heavy logic-to-UI binding — forms, data tables, multi-step wizards — where rewriting UI twice is pure cost with zero user-perceptible benefit.
When Compose Multiplatform Is the Wrong Call
- Consumer apps where iOS users expect Cupertino conventions and SwiftUI-quality animations.
- Apps using platform-native features heavily: Apple Maps with overlays, ARKit/RealityKit, complex camera UI, WidgetKit, Live Activities, Apple Watch companions.
- Apps where accessibility certification (WCAG, Section 508) requires deep platform-native a11y APIs that Compose Multiplatform on iOS still implements partially.
How It Compares to Alternatives
Flutter also renders with Skia, so the "non-native widget" tradeoff is essentially the same — except you write Dart instead of Kotlin, according to Callstack's comparison. If you've already standardized on Kotlin for Android and server work, Flutter forces a language switch that Compose Multiplatform doesn't.
React Native uses native widgets via a JavaScript bridge. It gives you better "native feel" than Skia-based frameworks but worse logic performance and weaker type safety than Kotlin. For teams that already employ web engineers, it remains a defensible choice; for Kotlin-heavy teams, it's a step backward.
Jetpack Compose (Android only) uses the same APIs as Compose Multiplatform but only targets Android. If you only ship Android, you don't need Compose Multiplatform — you need Jetpack Compose, full stop.
The practical reframe: Compose Multiplatform is not a SwiftUI replacement. It is a tool for choosing UI-sharing over platform fidelity when the tradeoff favors your product. Marek Mościchowski of Futured captures this directly: "For some products, strict design consistency and shared code are more important than perfectly native UX. For others, we still prefer fully native UIs and use KMP only for the shared foundation."
The decision depends on what your users care about, not on which tool got more conference talks.
Using KMP and Compose Multiplatform Together
Look at production KMP apps shipped in 2024 and 2025 and a pattern emerges: most use both. The JetBrains official template ships them together. Touchlab's production guidance assumes both. Community sample projects like John O'Reilly's PeopleInKotlin demonstrate the combination running in App Store builds. The kotlin multiplatform vs compose multiplatform question dissolves when you use both — the question becomes how much of each.
Here is the architectural split layer by layer:
| Layer | Technology | Typical Sharing | What Stays Platform-Specific |
|---|---|---|---|
| App shell / entry | Native | 0% | MainActivity, @main App shell |
| UI components & screens | Compose Multiplatform | 80–100% | Rare platform overrides |
| Navigation | Decompose / Voyager | 90–100% | OS back-handling bridges |
| State & ViewModels | KMP (commonMain) | 100% | None |
| Domain / business logic | KMP | 100% | None |
| Networking | Ktor | 100% | None |
| Local database | SQLDelight | 100% schema | Native driver |
| Authentication | Firebase Auth | 70–80% | Keychain / Keystore |
| In-app purchases | RevenueCat | 80% | Store SDK shims |
| Push notifications | Firebase Messaging | 70% | APNS registration, notification UI |
| Analytics & crash | Firebase / Crashlytics | 90% | SDK init |
The productivity leap comes from this combination, not from either tool alone. KMP eliminates business logic duplication. Compose Multiplatform eliminates UI boilerplate. The savings stack because the work disappears at different layers.
For a typical SaaS mobile app, the realistic split looks like this. Shared in commonMain and composeApp: authentication flows, dashboards, list and detail screens, settings, billing, onboarding, navigation, theming. Platform-specific: app lifecycle code, deep link handlers, widget extensions, OS-integrated features like Siri Shortcuts, App Shortcuts, and watch apps. The shared portion is the bulk of the app — the platform code is the connective tissue.
JetBrains' published samples and community projects demonstrate that total code sharing reaches 80–95% for B2B and data-heavy apps when both tools are used together. The figure varies based on how much native polish your iOS UX demands, but most teams pushing for shared UI land in that range.
A practical escape hatch worth knowing about: even when using Compose Multiplatform, you can drop into native UI per-screen. On iOS, you expose a SwiftUI view that hosts a ComposeUIViewController for shared screens, and you keep pure SwiftUI for screens that demand platform fidelity — complex camera capture, ARKit views, deeply Cupertino-styled flows. This hybrid is Pattern 5 in the next section, and it's the most common configuration for teams adopting KMP incrementally rather than starting fresh.
A production-ready KMP starter kit compresses the upfront setup cost considerably. The savings aren't marketing fluff — they reflect the actual time it takes to wire Ktor, SQLDelight, RevenueCat, Firebase, Koin, Compose Multiplatform, CI/CD, and the iOS framework export into a coherent build. Doing it once teaches you the architecture. Doing it from scratch for every project is a tax you can pay once and avoid forever.
The productivity leap happens when KMP and Compose Multiplatform work together. KMP eliminates business logic duplication. Compose Multiplatform eliminates UI boilerplate. Separately they save time. Together they change how you structure your entire app.
Choosing Your Setup Based on Team and Stage
Answer these eight questions in order. Your answers point to one of three setups: KMP-only with native UI, KMP + Compose Multiplatform, or KMP + hybrid (Compose MP plus selective native screens). There is no universal right answer — only the right answer for your team, your users, and your stage.
1. Is your team Kotlin-first or platform-specialist?
Why it matters: Compose Multiplatform's leverage depends on Kotlin fluency. Native UI's leverage depends on SwiftUI and Jetpack Compose expertise. You amplify whichever skill you already have.
How to answer: Count engineers comfortable shipping production iOS code in Swift versus Kotlin. Be honest about "comfortable" — tutorial-comfortable isn't production-comfortable.
Implication: Majority Kotlin → Compose Multiplatform. Balanced specialists → native UI per platform with KMP shared logic.
2. Is your app's value in a custom design system, or in platform-native polish?
Why it matters: Compose MP excels when your brand UI is the moat. Native UI excels when iOS users must feel they're using a real iOS app.
How to answer: Look at competitor apps in your category. If they all share a bespoke design language, you're design-driven. If they conform to platform conventions, you're platform-driven.
Implication: Design-driven → Compose Multiplatform. Platform-driven → native UI.
3. What's your stage — MVP validation or post-PMF scaling?
Why it matters: At MVP, you optimize for time-to-decision. At scale, you optimize for maintenance cost over multiple years.
How to answer: Have you validated the core value proposition with paying users yet?
Implication: Pre-PMF → KMP for logic plus the fastest UI path (often Compose MP if the team is Kotlin-fluent). Post-PMF → invest in whichever setup minimizes long-term duplication for your team composition.
4. How much of your app uses platform-native features?
Why it matters: HealthKit, ARKit, WidgetKit, Live Activities, App Intents, Apple Watch — these require native code regardless of cross-platform choice. If you're writing significant native code anyway, sharing UI saves less than you'd hope.
How to answer: List every iOS-only and Android-only feature your roadmap requires for the next 12 months.
Implication: High native feature density → KMP + native UI. Low density → KMP + Compose Multiplatform wins.
5. What's your iOS user expectations bar?
Why it matters: Consumer iOS users notice non-native scroll physics, keyboard behavior, and text selection within seconds of opening the app. B2B users typically don't.
How to answer: Read App Store reviews of competitors in your category. Do users complain about apps "feeling Android-like" on iOS?
Implication: Strict consumer iOS bar → native UI. B2B or design-led → Compose MP is acceptable.
6. How fast must you ship feature parity across platforms?
Why it matters: Compose Multiplatform forces parity by default — you write a feature once and both platforms get it. Native UI per platform allows platform-specific feature timing, which can be a feature or a bug depending on your business.
How to answer: Is "ship Android first, iOS later" acceptable to your business?
Implication: Strict simultaneous parity → Compose Multiplatform. Flexible → either approach works.
7. What's your CI/CD complexity tolerance?
Why it matters: KMP on iOS adds Gradle-Xcode integration complexity. Futured's production case study documents this as real, ongoing overhead — slower debug cycles on iOS compared to pure Swift, plus tooling discipline required to keep builds reliable.
How to answer: Does your team have someone who can own the build system end-to-end?
Implication: Low tolerance → simpler setup wins. Pre-built CI/CD pipelines (such as those shipped with production KMP starters) reduce this risk substantially, but they don't eliminate it.
8. What's your budget?
Why it matters: Building the bridge layer from scratch costs 122+ hours of senior engineer time. At realistic blended rates, that's a four-figure expense before you write a single feature.
How to answer: Compare your engineer-hour cost to the cost of a lifetime-access KMP starter tier.
Implication: If shipping fast matters more than $169–$349 of upfront cost, a pre-wired starter wins. If you genuinely enjoy Gradle archaeology, you can do it yourself.
If you answered Kotlin-first + design-driven + post-PMF + low native feature density + B2B users, your setup is KMP + Compose Multiplatform across the entire app. If you answered platform-specialists + native-driven + consumer iOS bar, your setup is KMP for shared logic with native UI on both platforms. If you answered mixed — some Kotlin fluency, some native polish requirements, some platform-specific features — your setup is the hybrid: KMP everywhere, Compose Multiplatform for the bulk of screens, and native UI for the handful that demand it. Most production teams land in the hybrid camp, even when they planned for one of the pure setups.
Five Production Patterns for KMP and Compose Multiplatform
Each pattern below describes a real team archetype, the configuration they use, the libraries that show up in their build.gradle.kts, and the tradeoff they've explicitly accepted.
Pattern 1: The Solo Founder Shipping an MVP
Who they are: One developer, mobile-only product, racing to validate a market.
What they use: KMP + Compose Multiplatform end-to-end. Often via a pre-wired starter kit to skip auth, payments, push, networking, and DI setup. The kit saves the documented 122+ hours that would otherwise go into wiring Ktor, SQLDelight, RevenueCat, Firebase, and Koin into a working Gradle structure with iOS framework export configured.
Why: Solo time is the binding constraint on everything. Shipping iOS and Android from one codebase doubles distribution without doubling work. Native iOS is a luxury you can add post-validation.
Tradeoff: Compose Multiplatform iOS edge cases will surface — text selection quirks, occasional gesture conflicts, the rare animation glitch. You accept them now and drop to native per-screen later, only on screens where users complain.
Pattern 2: The Small Kotlin-Heavy Team (2–3 Developers)
Who they are: Two or three engineers, mostly backend-Kotlin or Android-Kotlin, expanding to iOS without hiring a Swift specialist.
What they use: KMP shared module + Compose Multiplatform for roughly 70% of screens + native SwiftUI for the 30% that demand it (camera capture, deep iOS integrations, anything involving WidgetKit or App Intents).
Why: The team's Kotlin fluency is the leverage. Writing SwiftUI from scratch wastes it. But screens that touch platform-specific OS features warrant native code regardless of language preference.
Tradeoff: You maintain two UI paradigms in one repo. This requires Gradle-Xcode integration discipline. Mitigated by sticking to JetBrains' canonical structure: shared/, composeApp/, iosApp/, as the official template demonstrates. Don't invent your own structure; the canonical one solves problems you don't yet know you have.
Pattern 3: The Dual-Specialist Team
Who they are: Established team with at least one dedicated Swift specialist on iOS and one Kotlin/Android specialist. Usually a mid-stage startup or an established product company.
What they use: KMP shared module for logic only — Ktor for networking, SQLDelight for persistence, shared business rules, shared auth flows. Native UI on both platforms: SwiftUI on iOS, Jetpack Compose on Android.
Why: Specialists ship fastest in their native stack. Forcing the iOS dev into Kotlin Compose throws away their leverage and their hiring story. KMP still eliminates business logic duplication, which is the lion's share of the bug surface. Florina Muntenescu of Google describes this as the canonical entry point on the Android Developers Blog: "With Kotlin Multiplatform, you can share business logic across Android and iOS while keeping UIs native. You don't need to rewrite your entire app to start."
Tradeoff: Sharing lands at 60–80% instead of 80–95%. UI parity requires coordination between specialists, not enforcement by the framework. Some teams find this preferable because parity-by-coordination produces better platform-specific UX than parity-by-shared-code.
Pattern 4: The Design-System-First Company
Who they are: B2B SaaS, internal tools, dashboards, design tools, configurators — products where brand consistency is the product strategy.
What they use: KMP + Compose Multiplatform, aggressive UI sharing pushed to 90%+. Design tokens, components, and screens authored once. Often paired with a tiered KMP starter option at the upper tier to skip the design system bootstrap as well.
Why: The design system is the moat. Platform-native variation undermines the product because users expect their workflow to look identical whether they're on a phone, tablet, or desktop view. Compose Multiplatform's Skia rendering ensures pixel-identical output across iOS and Android — which is a feature here, not a bug.
Tradeoff: iOS users may notice non-Cupertino conventions. Acceptable for B2B users who prioritize cross-device workflow consistency over platform-native idioms. Consumer products in this pattern face more pushback.
Pattern 5: The Flagship Native App Adding Cross-Platform Features
Who they are: Established iOS or Android app with a mature native codebase, adding the other platform or sharing only specific features.
What they use: KMP shared module for backend communication, persistence, and auth. Existing native UI stays untouched. New shared features — onboarding flows, in-app browsers, modal sequences, settings — may use Compose Multiplatform selectively, embedded via ComposeUIViewController on iOS.
Why: The flagship UX is already a competitive asset. Rewriting it would be value destruction. But the next 10 features don't need to be built twice, and the team can adopt KMP feature-by-feature instead of in a risky rewrite.
Tradeoff: Incremental KMP adoption requires careful interop discipline — Swift developers depend on a Kotlin-generated framework they can't easily debug from Xcode. The Badoo Tech Blog critique applies here: tooling and debugging across the Kotlin/Native boundary remains imperfect, even in 2025. Teams in this pattern invest in build observability and developer documentation more than other patterns.
Your pattern is in this list. If it isn't, you're probably in transition between two — which is fine. Pick the destination you're heading toward and structure the project for that, not for where you are today. KMP is the universal base layer in every pattern. The Compose Multiplatform question is the variable. Decide that one based on your team and your users.