Seven Peaks Insights

Compose Multiplatform 1.8.0: What’s new and what you need to know

Introduction

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:

  • The key changes introduced in version 1.8.0
  • Critical breaking changes that could affect your projects
  • Practical migration tips to make your upgrade seamless

Let’s dive in.

Key changes in Compose Multiplatform 1.8.0

iOS is now Stable and Production-ready

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:

  • Optimized layout measuring and rendering.
  • Better text baseline alignment.
  • More reliable Modifier behavior
  • Smoother scrolling and gesture responsiveness.

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.

SavedState replaces Bundle for Navigation Arguments

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:

  • Safer (nullability is clearer)
  • Platform-agnostic (not tied to Android)
  • Consistent across KMP targets

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.

Our experience:

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?

  • Removed String-based routes:

data object BuildingDetails : BuildingsScreen(

    route = "building_details",

    deepLinks = listOf(

        NavDeepLink("$DEEPLINK/building_details")

    )

)

 

  • Prepared Type-safe routes after rid of String-based ones:

sealed class BuildingsScreen {


    @Serializable

    object ROUTE : BuildingsScreen()


    @Serializable

    class BuildingDetails(

        val buildingId: String

    ) : BuildingsScreen()

...

}

 

  • Updated the methods related to navigating to screens:

navController.navigate(

// Before

    route = BuildingsScreen.BuildingDetails.route

// After    

    route = BuildingsScreen.BuildingDetails

)

 

  • And the final step - handling data during navigation:

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.

Line-height alignment across platforms

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

        )

    )

)



 

runOnIdle now runs on the UI thread

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.

Main thread enforcement in tests

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:

  • onNodeWithTag()
  • performClick()
  • assertTextEquals()

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. 

Introduction of the new Clipboard interface

What changed?

A new Clipboard interface replaces the deprecated ClipboardManager.

Why does it matter?

The new interface supports suspend functions and is compatible with all targets, including web, enhancing asynchronous clipboard operations.

What do developers need to adjust?

Transition to using the new Clipboard interface for clipboard operations in your applications.

Deprecation of ComposeUIViewControllerDelegate on iOS

The ComposeUIViewControllerDelegate API has been deprecated.

Why does it matter?

Developers should now override UIViewController class methods via the parent view controller, adhering to Apple's recommended practices.

What do developers need to adjust?

Refactor your iOS code to override methods in the parent UIViewController instead of using the deprecated delegate.

Removal of platformLayers option on iOS

What changed?

The experimental platformLayers option has been removed.

Why does it matter?

The alternative layering mode introduced in version 1.6.0 is now the default behavior on iOS, rendering the option obsolete.

What do developers need to adjust?

No action is required unless your project explicitly relied on the platformLayers option.

Layout & Platform improvements

Several improvements refine the behavior of Compose UIs across platforms:

  • TextField baseline alignment is now consistent across platforms.
  • Insets support is improved in Compose for iOS and Desktop.
  • Better text input responsiveness in Compose Web.
  • Fixed layout bugs related to Modifier.alignByBaseline() on iOS.
  • Compose Web now handles awaitPointerEventScope in more browsers.
  • Compose Multiplatform now uses Skia 1.3.0, leading to better rendering performance and stability.

These changes improve visual consistency and responsiveness, especially when reusing design elements across screen sizes and platforms.

 

Breaking changes you should know

The Kotlin K2 Compiler is now required

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.

What you need to know:

  • Kotlin 2.1.0+ is mandatory. All native (including iOS) and web Compose targets must use it.
  • K1-based builds are no longer supported for these targets.
  • You must recompile all your Compose libraries using K2 to avoid ABI mismatches.

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.

Our experience:

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:

  • Update Android Gradle Plugin to version 8.8.2
  • Update Room to the last available version (2.7.1 at the moment of update), as well as Sqlite driver, and migrate from Kapt to KSP.
  • Removed the entire composeOptions section from app gradle configuration and added org.jetbrains.kotlin.plugin.compose to gradle plugins.
  • Updated Koin DI to version 4.0.2

Material Icons must be declared explicitly

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.

Our experience:

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)

}

 

runOnIdle now executes on UI thread

If your tests relied on the implicit waitForIdle() call, add it explicitly after runOnIdle():


runOnIdle {

    // your test code

}

waitForIdle()

 

Migration tips

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.

Conclusion

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.

2023_Head of Mobile_Dmitrii_02

Dmitrii Afonin (Dmitrii), Head of Mobile
at Seven Peaks

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.