Compose Multiplatform Cross-Platform Development Guide

Compose Multiplatform Development for Cross-Platform Apps

Compose Multiplatform development extends Jetpack Compose beyond Android to iOS, desktop, and web platforms with a single shared UI codebase. Therefore, teams write composable functions once and deploy them across every major platform without sacrificing native performance. As a result, development velocity doubles while maintaining platform-specific experiences where they matter most.

Shared UI Architecture

The framework builds on Kotlin Multiplatform to share not just business logic but the entire UI layer. Specifically, composable functions use the same declarative API regardless of the target platform, with platform-specific rendering handled by the framework. Moreover, the compiler generates optimized code for each platform rather than using a cross-platform rendering bridge.

This approach differs fundamentally from React Native and Flutter. However, unlike those frameworks, Compose Multiplatform compiles to native code on each platform. Consequently, UI performance matches hand-written native implementations while sharing the developer experience.

Compose Multiplatform development shared UI
Shared composable functions render natively on each target platform

Building Shared Composables with Expect/Actual

The expect/actual mechanism bridges platform differences while keeping the API surface consistent. Additionally, expect declarations define the interface in common code while actual implementations provide platform-specific behavior. For example, file system access, camera integration, and biometric authentication use this pattern extensively.

// commonMain — shared composable
@Composable
fun ProductListScreen(viewModel: ProductViewModel) {
    val products by viewModel.products.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        Text(
            text = "Products",
            style = MaterialTheme.typography.headlineMedium,
            modifier = Modifier.padding(bottom = 12.dp)
        )
        if (isLoading) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
        } else {
            LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                items(products) { product ->
                    ProductCard(product = product, onClick = { viewModel.select(product) })
                }
            }
        }
    }
}

// commonMain — expect declaration
expect class PlatformContext

expect fun getPlatformName(): String

// androidMain — actual implementation
actual typealias PlatformContext = android.content.Context

actual fun getPlatformName(): String = "Android " + android.os.Build.VERSION.SDK_INT

// iosMain — actual implementation
actual class PlatformContext

actual fun getPlatformName(): String = "iOS " + UIDevice.currentDevice.systemVersion

This pattern keeps platform logic isolated. Therefore, common code remains testable and platform-agnostic while actual declarations handle native integration points.

Navigation and State Management

Cross-platform navigation requires careful abstraction since each platform has different navigation paradigms. Furthermore, libraries like Voyager and Decompose provide multiplatform navigation with back stack management, deep linking, and lifecycle awareness. Specifically, Decompose uses a component-based architecture that separates navigation logic from UI rendering.

State management follows the standard Kotlin Flow and StateFlow patterns across all platforms. Meanwhile, ViewModel-like components hold UI state and survive configuration changes on Android while providing consistent behavior on iOS and desktop.

Cross-platform mobile app development
Shared navigation and state management across Android, iOS, and desktop

Compose Multiplatform Development Platform Integration and Native APIs

Real-world applications need access to platform-specific APIs for cameras, sensors, notifications, and storage. Additionally, Kotlin Multiplatform's interoperability with Swift and Objective-C allows calling iOS frameworks directly from shared code. For example, Core Location on iOS and FusedLocationProvider on Android integrate through expect/actual declarations with a common LocationProvider interface.

Desktop targets access JVM libraries and native system APIs. Moreover, web targets compile to JavaScript through Kotlin/JS, enabling the same composables to render in browser environments with canvas-based or DOM-based rendering.

Native platform API integration
Platform-specific APIs integrate through expect/actual declarations

Related Reading:

Further Resources:

In conclusion, Compose Multiplatform development enables true cross-platform UI sharing with native performance on every target. Therefore, adopt this framework when building applications that target Android, iOS, desktop, and web from a single Kotlin codebase.

Scroll to Top