Compose Multiplatform has been making serious strides in unifying UI development across Android, desktop, and iOS using a shared Kotlin codebase. As a Mobile Engineer working across platforms, the ability to build high-quality, responsive UIs with a single codebase is a game-changer, both in terms of productivity and code maintainability.
With the release of Compose Multiplatform 1.8.0, JetBrains brings a wave of enhancements that are not just evolutionary but transformational. This version is not just an update - it’s a signal that Kotlin’s promise of sharing UI logic between Android, iOS, desktop, and web is now production-grade.
In this article, I’ll walk you through:
Let’s dive in.
After over a year of active development, Compose for iOS is now officially stable. This means you can ship apps to the App Store using Compose UI on iOS with confidence in API stability and ongoing support.
iOS support includes:
Under the hood, Apple-specific rendering quirks and performance issues have been resolved. For example, views are better integrated with UIKit via ComposeUIViewController, and scrolling now behaves more naturally on iOS.
This milestone unblocks true multiplatform UIs with a single Kotlin codebase and enables shared design systems without relying on SwiftUI or UIKit directly.
A subtle but impactful change: Compose’s navigation framework now uses SavedState instead of Android's Bundle for route arguments. This API shift makes argument parsing:
You’ll now use:
val userId = arguments.read { getStringOrNull("userid") } |
instead of:
val userId = arguments?.getString("userid") |
The SavedState API aligns well with state persistence goals in multiplatform apps and positions Compose for navigation parity on desktop, iOS, and beyond.
Even though the change looks quite minor and can be applied through the entire codebase quickly, we decided to also migrate to type-safe navigation at the same time (as when we started our current projects with KMP/CMP, type-safe navigation was not available). But it was worth the time.
So, how did we make this happen?
data object BuildingDetails : BuildingsScreen( route = "building_details", deepLinks = listOf( NavDeepLink("$DEEPLINK/building_details") ) ) |
sealed class BuildingsScreen { @Serializable object ROUTE : BuildingsScreen() @Serializable class BuildingDetails( val buildingId: String ) : BuildingsScreen() ... } |
navController.navigate( // Before route = BuildingsScreen.BuildingDetails.route // After route = BuildingsScreen.BuildingDetails ) |
val buildingId = it.toRoute<BuildingsScreen.BuildingDetails>().buildingId BuildingDetailsScreen( buildingId = buildingId ) |
In the long run, these changes will improve the readability of your code and reduce the time required for maintenance.
The LineHeightStyle.Alignment API is now supported on all platforms. Developers can now configure text line alignment consistently across Android, iOS, and other platforms.
Apply LineHeightStyle.Alignment in your text components to achieve desired alignment:
Text( text = "Sample Text", style = TextStyle( lineHeight = 20.sp, lineHeightStyle = LineHeightStyle( alignment = LineHeightStyle.Alignment.Center ) ) ) |
A key change in Compose test APIs: runOnIdle now executes on the main UI thread. Previously, this function could run on a background thread, which sometimes masked thread-safety issues in stateful UIs.
This shift brings test behavior in line with real-world runtime expectations and prevents hidden race conditions in test setups.
Many Compose test APIs now enforce main thread execution. If you try to use them off the main thread, an exception will be thrown.
For example:
These APIs now require wrapping in:
runOnIdle { ... } |
or
runOnUiThread { ... } |
Tweaking current test suites might be needed to make tests more reliable, especially if they've been a bit too forgiving in the past.
A new Clipboard interface replaces the deprecated ClipboardManager.
The new interface supports suspend functions and is compatible with all targets, including web, enhancing asynchronous clipboard operations.
Transition to using the new Clipboard interface for clipboard operations in your applications.
The ComposeUIViewControllerDelegate API has been deprecated.
Developers should now override UIViewController class methods via the parent view controller, adhering to Apple's recommended practices.
Refactor your iOS code to override methods in the parent UIViewController instead of using the deprecated delegate.
The experimental platformLayers option has been removed.
The alternative layering mode introduced in version 1.6.0 is now the default behavior on iOS, rendering the option obsolete.
No action is required unless your project explicitly relied on the platformLayers option.
Several improvements refine the behavior of Compose UIs across platforms:
These changes improve visual consistency and responsiveness, especially when reusing design elements across screen sizes and platforms.
Compose 1.8.0 fully adopts Kotlin’s next-gen K2 compiler across Native and Web targets. This change significantly improves compile-time performance, enhances type inference, and unlocks future Kotlin features.
For Android-only projects, this may not cause immediate friction. But in multiplatform modules, you’ll need to update kotlinCompilerPluginClasspath, Kotlin Gradle Plugin version, and possibly refactor incompatible code.
In general, if you use only the common Google and Jetbrains SDKs, there will not be problems with migration. We use Gradle Versions Catalogs, which I personally found useful to keep all dependencies in one place and easy to manage in cases like this. And as our team continuously maintains dependency versions over time, after upgrading the version of Kotlin to 2.1.20 we’ve got no issues at all, the app built as usual with the new version.
There are steps we’ve taken at the same time to keep migration smooth:
To reduce transitive bloat and align with the K2 plugin’s new architecture, the material-icons-core library is no longer bundled automatically.
If your project uses any Material icons, you must now declare them explicitly:
implementation("org.jetbrains.compose.material:material-icons-core:<version>") |
This change encourages better dependency hygiene, especially important for iOS and web targets where binary size and asset loading can be sensitive.
We actively use the Material Icons library, but extended dependency. It did not require additional steps as the dependency was already mentioned in our feature module gradle build configs.
This is how our setup looks:
libs.versions.toml
[versions] compose-plugin = "1.8.0" ... [plugins] jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } ... |
build.gradle
plugins { ... alias(libs.plugins.jetbrainsCompose) ... } ... commonMain.dependencies { implementation(compose.materialIconsExtended) } |
If your tests relied on the implicit waitForIdle() call, add it explicitly after runOnIdle():
runOnIdle { // your test code } waitForIdle() |
Upgrade Kotlin Version: Ensure your project uses Kotlin 2.1.20 or newer to maintain compatibility.
Explicit Dependencies: Add material-icons-core explicitly if your project relies on it.
Update Navigation Logic: Refactor navigation code to utilize SavedState for state management.
Revise Tests: Review and adjust tests affected by changes in runOnIdle behavior and time advancement handling.
Adopt New Clipboard Interface: Transition to the new Clipboard interface for cross-platform clipboard operations.
Leverage Variable Fonts: Utilize variable fonts to enhance UI flexibility and performance.
If you already use one of the last alpha versions of the Compose Multiplatform library in your project, migration to the stable one will not take longer than updating your Gradle Versions Catalogs and rebuilding the project. Otherwise, it can require a significant time investment into refactoring the code.
Compose Multiplatform 1.8.0 marks a major milestone with stable iOS support, performance improvements, and refined APIs. While the migration should be smooth for most projects, pay close attention to breaking changes in touch handling and threading.
By our experience with Kotlin Multiplatform and Compose Multiplatform, we can save up to 50% of the development time when making Android + iOS applications, as most of the functionality is production ready and works out of the box.
By following the best practices outlined here, you can ensure a seamless upgrade and take full advantage of Compose Multiplatform’s growing capabilities.
Dmitrii Afonin (Dmitrii), Head of Mobile Dmitrii is a head of mobile with 15+ years of experience in Android development and backend systems. He has led projects end-to-end, with expertise in architecture design, Java/Kotlin, and Agile workflows. Passionate about solving technical challenges and continuous learning. |