The Ultimate Guide to Kotlin Multiplatform (KMP)
1. Introduction: The Multiplatform Revolution
Kotlin Multiplatform (KMP) has fundamentally shifted how we think about code reuse. For years, the industry chased the "Write Once, Run Anywhere" (WORA) dream, which often resulted in "Write Once, Debug Everywhere." KMP takes a different path: Share the logic, keep the UI native.
What is Kotlin Multiplatform (KMP)?
Unlike cross-platform frameworks that wrap your app in a "black box" or a custom rendering engine, Kotlin Multiplatform is an SDK designed to simplify the development of cross-platform projects. Developed by JetBrains, it allows you to maintain a single codebase for the "boring" parts of your app (business logic, networking, and data persistence) while leveraging the native strengths of each platform.
When you write KMP code, you aren't writing for a virtual machine that sits on top of the OS. You are writing Kotlin that compiles directly to:
- JVM bytecode for Android.
- Native binaries (via LLVM) for iOS and macOS.
- WebAssembly (Wasm) or JavaScript for the browser.
The Philosophy: Share Logic, Not UI
The biggest pain point in mobile development has always been the "double-work" of implementing the same business rules in both Kotlin and Swift.
KMP solves this by focusing on the domain and data layers.
- The Shared Layer: Networking (Ktor), Database (Room), and Business Logic live in a
commonMainmodule. - The Native Layer: Your Android app uses Jetpack Compose, and your iOS app uses SwiftUI. They both consume the same shared "source of truth," but the user gets the 60fps, high-fidelity experience of a truly native app.
The Ecosystem in 2026: Stability and Maturity
As of 2026, the KMP ecosystem has moved past the "experimental" phase. With Google officially supporting KMP for Android/iOS logic sharing and JetBrains stabilizing Compose Multiplatform for iOS, the risk of adoption has vanished.
Major players like Netflix, McDonald's, and Cash App have already proven that KMP scales in high-concurrency, production environments. It is no longer a question of if KMP is ready, but how you can leverage it to ship faster.
2. Core Concepts: The Engine Behind KMP
Understanding how Kotlin code runs on an iPhone without a Virtual Machine is the first step to mastering the ecosystem. KMP relies on a unique compilation pipeline and a "contract-based" shared logic system.
The Expect/Actual Mechanism
The cornerstone of Kotlin Multiplatform is the expect/actual mechanism. Think of this as a compile-time contract.
expect(The Contract): You define a declaration in thecommonMainsource set. You are telling the compiler, "I expect every platform to provide an implementation for this."actual(The Fulfillment): You provide the platform-specific implementation iniosMain,androidMain, etc.
Unlike standard interfaces, expect/actual allows you to access platform-specific types (like platform.UIKit.UIDevice on iOS or android.os.Build on Android) directly in your Kotlin code.
// In commonMain
expect fun getPlatformName(): String
// In iosMain
import platform.UIKit.UIDevice
actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()
// In androidMain
import android.os.Build
actual fun getPlatformName(): String = "Android ${Build.VERSION.SDK_INT}"expect/actual for business logic. Reserve expect/actual for low-level platform "shims" like logging or local file paths.Project Structure & Source Sets
A KMP project is organized into source sets. This allows the Kotlin Gradle Plugin to manage which code belongs to which target.
The standard hierarchy (using the default hierarchy template) looks like this:
commonMain: The "brain" of the app. 80–90% of your code lives here.nativeMain: Shared code for all native targets (iOS, macOS, Linux).iosMain: Specific to Apple’s ecosystem.androidMain: Specific to the Android JVM and Android APIs.
Compilation Targets: From JVM to Native & Wasm
One of KMP's superpowers is how it handles different execution environments:
- Kotlin/JVM: Compiles to standard Java bytecode for Android and backend services.
- Kotlin/Native: Uses the LLVM toolchain to produce standalone binaries (
.frameworkfor iOS). This is how KMP achieves native performance with no overhead. - Kotlin/Wasm: The newest frontier. By compiling to WebAssembly, KMP can run at near-native speeds in the browser, bypassing the performance bottlenecks of JavaScript.
Swift Interoperability (Swift Export)
In 2026, the bridge between Kotlin and Swift has evolved. While earlier versions relied on an Objective-C intermediate layer, the modern Swift Export allows Kotlin code to be seen as native Swift modules. This means suspend functions map directly to Swift's async/await, and sealed classes map to native Swift Enums with associated values.
3. KMP vs. Compose Multiplatform (CMP)
One of the most frequent questions in the ecosystem is: "Is Kotlin Multiplatform the same thing as Compose Multiplatform?"
The answer is no. While they are developed by JetBrains and work seamlessly together, they serve two distinct purposes in your architecture.
Kotlin Multiplatform (The Foundation)
Kotlin Multiplatform (KMP) is the core technology that allows you to share non-UI logic. It handles your networking, data persistence, and business rules. When you use "KMP only," you are sharing the "brain" of the app but building the "face" (UI) twice:
- Android: Jetpack Compose
- iOS: SwiftUI
Compose Multiplatform (The UI Layer)
Compose Multiplatform (CMP) is a declarative UI framework built on top of KMP. It extends Jetpack Compose beyond Android, allowing you to share your UI code across iOS, Desktop (JVM), and Web (Wasm/JS).
The Rendering Engine: How Shared UI Works
Unlike React Native (which maps to native components) or Flutter (which draws everything on its own canvas), Compose Multiplatform on iOS uses Skia (via the Skiko library). This allows Compose to render UI with pixel-perfect consistency across platforms while still running as a native binary.
Decision Matrix: Shared UI or Native UI?
The "Senior" approach is to choose based on the project requirements rather than personal preference.
| Strategy | When to Choose | UI Tech Stack |
|---|---|---|
| Traditional KMP | High-fidelity apps, strict "platform-native" look and feel requirements, or integrating into existing apps. | KMP (Logic) + SwiftUI / Jetpack Compose (native UI) |
| Full Compose (CMP) | Speed to market, internal tools, or apps where brand consistency is more important than platform-specific nuances. | KMP (Logic) + Compose Multiplatform (UI) |
Interoperability: The Best of Both Worlds
You don't have to choose just one. The ecosystem supports Interoperability.
- UIKit in Compose: You can embed a native
MKMapVieworUIImagePickerinside a Compose screen. - Compose in SwiftUI: You can export a specific Compose-based feature (like a complex chart or a custom editor) as a
UIViewControllerto be used inside a standard SwiftUI app.
4. Technical Comparison: KMP vs. Flutter vs. React Native
When choosing a cross-platform strategy, most teams evaluate three main contenders: Flutter, React Native, and Kotlin Multiplatform. While all three aim to reduce code duplication, their underlying architectures are fundamentally different.
The Comparison Matrix
This table breaks down the core trade-offs between the three most popular cross-platform frameworks in 2026.
| Feature | Kotlin Multiplatform (KMP) | Flutter | React Native |
|---|---|---|---|
| Primary Language | Kotlin | Dart | JavaScript / TypeScript |
| UI Rendering | Native (UIKit/Jetpack) or Skia | Skia (Custom Canvas) | Native UI via Bridge/JSI |
| Performance | Native (No overhead) | High (Optimized C++) | Moderate (JS Bridge cost) |
| Interoperability | 100% Native Interop | Via Platform Channels | Via Native Modules |
| Code Sharing | Business Logic / Optional UI | UI and Logic | UI and Logic |
| Adoption Style | Incremental (Module by module) | All-or-nothing (Greenfield) | Greenfield or Integrated |
1. KMP: The Native Multiplatform
KMP is unique because it doesn't try to replace the native platform; it augments it. By compiling to native binaries via LLVM, your shared code runs with the same performance as if it were written in Swift or Java.
- Pros: Access to 100% of native APIs, zero performance penalty, and easy integration into existing enterprise apps.
- Cons: Higher learning curve for iOS-only teams not familiar with Kotlin.
KMM vs. KMP: What Happened to "Mobile"?
If you have been researching this space for a while, you likely encountered the term KMM (Kotlin Multiplatform Mobile). In 2023, JetBrains officially deprecated the KMM brand in favor of the unified KMP (Kotlin Multiplatform) name.
Why the change? Originally, "KMM" specifically referred to sharing code between Android and iOS. However, as Kotlin’s reach expanded to Desktop (JVM), Web (Wasm), and Server-side, the "Mobile" suffix became too restrictive.
- KMM: An old branding term for mobile-only sharing.
- KMP: The current, all-encompassing name for the technology, regardless of whether you are targeting iOS, Android, Web, or Desktop.
Today, whether you are building a mobile app or a cross-platform desktop tool, the technology, plugins, and libraries are all under the KMP umbrella.
2. Flutter: The Canvas-Based Contender
Flutter uses the Skia/Impeller engine to draw every pixel on the screen. It bypasses native UI components entirely.
- Pros: Incredible development speed (Hot Reload) and perfect UI consistency across platforms.
- Cons: Large binary sizes and a "non-native" feel in complex gestures or OS-specific UI behaviors.
3. React Native: The Ecosystem Giant
React Native bridges the gap between JavaScript and native components. With the move to the New Architecture (TurboModules and Fabric), performance has improved significantly.
- Pros: Massive library ecosystem (NPM) and a huge pool of available developers.
- Cons: Managing the "bridge" can become a performance bottleneck in data-heavy applications.
The "Killer Feature": Incremental Adoption
The deciding factor for many senior engineers is risk management. If a Flutter or React Native project fails, you often have to rewrite the entire app. With KMP, you can start by sharing a single data model or an analytics library. If you don't like it, you haven't lost the native foundation of your app.
5. The Essential KMP Tech Stack (2026)
One of the greatest strengths of Kotlin Multiplatform is its vibrant library ecosystem. Unlike the early days of KMP, we now have a "standard stack" that is battle-tested and ready for production. To keep your app truly multiplatform, you must use libraries that support the commonMain source set.
The Core Libraries Matrix
For a quick start, these are the industry-standard choices for every major architectural layer.
| Layer | Recommended Library | Maintained by |
|---|---|---|
| Networking | Ktor | JetBrains |
| Persistence | Room or SQLDelight | Google / Cash App |
| Dependency Injection | Koin | Arnaud Giuliani & Community |
| Concurrency | Kotlin Coroutines | JetBrains |
| Serialization | Kotlinx Serialization | JetBrains |
| Image Loading | Coil or Landscapist | Community |
Networking: Ktor
Ktor is an asynchronous HTTP client built entirely on Coroutines. It is engine-based, meaning it uses OkHttp on Android and NSURLSession (Darwin) on iOS automatically.
Why it wins: It’s type-safe, highly extensible via plugins, and supports WebSockets and streaming out of the box.
Persistence: SQLDelight vs. Room
- SQLDelight: Generates typesafe Kotlin APIs from your SQL statements. It is the "purist" choice for senior devs who want full control over their schema.
- Room Multiplatform: Google’s official persistence library now supports KMP. It’s an excellent choice for teams migrating from native Android who want to keep their existing DAO patterns.
Dependency Injection: Koin
Koin is a pragmatic, lightweight DI framework. Unlike Dagger/Hilt, it doesn't rely on heavy code generation, which makes it much faster to compile in a multiplatform context and easier to configure for iOS targets.
Concurrency: Coroutines & Flow
Multi-threading in KMP used to be a headache due to the old Kotlin/Native memory manager. In 2026, those days are gone. Kotlin Coroutines provide a unified way to handle background work. Combined with StateFlow and SharedFlow, you can create reactive view models that SwiftUI and Jetpack Compose can observe natively.
Serialization: Kotlinx.Serialization
The official Kotlinx Serialization library is the standard for parsing JSON. It works without reflection, which is a requirement for Kotlin/Native and ensures your app remains fast and lightweight on iOS.
6. Architectural Patterns in KMP
Choosing the right architecture in Kotlin Multiplatform dictates how much code you actually get to share. The golden rule is to push as much logic down into commonMain as possible, leaving the native UI layers as "dumb" rendering engines.
The Shared ViewModel: MVVM vs. MVI
Historically, KMP developers kept ViewModels platform-specific. In 2026, the industry standard is the Shared ViewModel, allowing you to share up to 85% of your presentation logic. But which pattern is best?
- MVVM (Model-View-ViewModel): The traditional Android approach. While familiar, having multiple observable properties (e.g.,
isLoading,userData,errorMessage) can lead to race conditions when observed by different UI frameworks (Compose vs. SwiftUI). - MVI (Model-View-Intent): The preferred architecture for modern KMP. MVI enforces a Unidirectional Data Flow (UDF). The UI sends "Intents" (actions) to the ViewModel, which emits a single, immutable
ViewState. Because SwiftUI and Jetpack Compose are both declarative, consuming a single stream of state is infinitely safer and easier to debug.
State Management: Bridging the Reactive Gap
To pass state from KMP to your native UI, Kotlin Coroutines and Flow are the undisputed champions. You expose your MVI state as a StateFlow.
However, Swift doesn't natively understand StateFlow. To solve this, the ecosystem relies on:
- SKIE (by Touchlab): A compiler plugin that automatically bridges Kotlin Coroutines and Flows into native Swift
async/awaitandCombinepublishers. It eliminates the boilerplate of writing custom wrappers. - KMP-NativeCoroutines: An alternative library by Rick Clephas that generates Swift-friendly concurrency code from your Kotlin definitions.
Navigation: Native vs. Shared
Navigation is the hardest architectural decision in any cross-platform app. You have two main routes:
1. Native Navigation (The Purist Approach)
You handle navigation entirely in the platform layer.
- Android: Jetpack Navigation.
- iOS: Swift Navigation or
NavigationStack.
Pros: 100% native transitions and deep-linking integration.
Cons: You have to write your routing logic twice.
2. Shared Navigation (The Pragmatic Approach)
You manage the backstack and routing inside commonMain.
- Decompose: The industry standard for enterprise apps. It handles lifecycle management and state preservation across configuration changes without depending on the UI framework.
- Voyager: A pragmatic, Compose-first navigation library. If you are using Compose Multiplatform for your UI, Voyager is the easiest way to handle multiplatform routing.