25th July 20265 min read

Kotlin Multiplatform with Compose: notes from an experiment

I have been writing React Native for over a decade now. Long enough to have opinions about it, long enough to have built a component library on top of it twice, and long enough to know exactly where it gets thin. I have also spent enough time with Flutter to have working knowledge rather than an opinion borrowed from a conference talk.

So when I keep seeing the same rising sentiment about Kotlin Multiplatform in the community, I wanted to actually check it instead of nodding along. I spent some time building with KMP and Compose Multiplatform, with a shared UI, and running it on Android, iOS and Web.

Short version: it is a good option now.

What I built

A small swipe-and-match app. Card stack with drag gestures, spring physics on release, a tab bar, a matches list, a profile screen with a slide-over. Nothing product-shaped, but deliberately chosen: card swiping is the cheapest way to find out whether a cross-platform UI layer is actually smooth or just claims to be. Gestures and animation are where these frameworks either hold up or fall apart.

One Compose codebase, three targets running side by side. Android emulator on the left, web build in a browser in the middle, iOS simulator on the right.

First impressions

Pretty performant. I pushed on animation and gesture-level interactions specifically, because that is where cross-platform stories usually get vague. Frame rates held. The drag-to-swipe felt right on iOS, which is the target I was most sceptical about.

It sits closer to Flutter, but more native. Compose Multiplatform draws its own widgets, same as Flutter does. So the pixel-identical-everywhere tradeoff is the same tradeoff. The difference is what surrounds it: Kotlin is a first-class language on Android already, the shared code compiles to native without an interpreter or a bridge, and stepping out of the shared UI into real platform UI is a supported move rather than an escape hatch.

Web works, but it is the youngest target. Compose Multiplatform for web is in beta. It ran, it was fine for a demo, I would not ship a public marketing surface on it today.

The actually interesting part

The thing that makes KMP different from every other cross-platform framework I have used is that "how much do you share" is a dial, not a decision you make once at the start.

You can begin with a shared UI in Compose, get to something running on all platforms quickly, and then peel back per platform without rewriting the foundation:

  • Keep all your logic in Kotlin. Models, networking, state, business rules, tested once.
  • Render with SwiftUI on iOS, because sometimes you want the real thing.
  • And inside those SwiftUI views, embed a shared Compose component. The pieces compose in both directions.

That last one is the bit I did not expect to work as cleanly as it does. A shared component can live inside a native screen, and a native screen can live inside a shared app. You are not choosing between "cross-platform app" and "native app". You are choosing, per screen, how much you want to hand over.

With React Native, brownfield integration is a well-trodden path, but the direction of travel is usually one-way and the seams are real. With Flutter, add-to-app exists and works, but you are embedding an engine. KMP was designed for the mixed case from day one, which is why it has the least friction of the three when you are adding to an app that already exists.

Where it stands against React Native and Flutter

I put together a feature-by-feature comparison across the three while the details were fresh. Fourteen rows: animation, third-party libraries, native integrations, web, desktop, rendering model, performance, hot reload, language, state management, testing, app size, community, and brownfield integration.

Feature comparison table across React Native, Flutter and Kotlin Multiplatform, covering animation, libraries, native integrations, web and desktop support, rendering model, performance, hot reload, language, state, testing, app size, community and brownfield integration

Too small to read here. Open the full comparison for the readable version.

A few honest calls out of that table, all of them things I would want to know before starting a project:

  • React Native still wins on ecosystem and hiring. Ten years of npm packages and the largest talent pool of the three is not a small advantage.
  • Flutter still wins on tooling cohesion. Stateful hot reload and built-in widget testing remain the best developer experience of the three.
  • KMP wins on the shape of the code. Shared logic that compiles to native with no bridge, plus the freedom to render natively wherever it matters.
  • KMP's weakest spot is the ecosystem. Fewer ready-made multiplatform libraries, more expect/actual wrappers you write yourself, and no true hot reload on the Kotlin/Native side yet.

That last point is the real cost of picking it today, and it is the kind of cost that shrinks every release. JetBrains and Google are both behind it, and the Jetpack libraries now ship multiplatform, which is a strong signal about where this is heading.

Where I land

I am not migrating anything. Ten years of React Native experience is not a sunk cost, it is a working advantage, and for most of what I build it is still the right pick.

But the reason I keep coming back to KMP in my head is the dial. Most cross-platform frameworks ask you to commit to a rendering model up front and then live with it. KMP lets you share the expensive part, the logic, and stay honest about the UI on a screen-by-screen basis. That is a better default than the industry has had so far.

If you have an existing native app and you have been waiting for a sane way to share code without swallowing a framework whole, this is the one to look at.

Comparison is a snapshot as of mid-2026. Library and platform-target maturity shifts quickly in this space, re-check before betting a roadmap on any single row.