Professional Splash Screens for Compose Multiplatform, configured in seconds.
Creating a seamless startup experience in Compose Multiplatform is notoriously difficult. Between the native Android SplashScreen API and iOS UILaunchScreen, developers often face a "white flash" gap between the native boot sequence and the Jetpack Compose Multiplatform runtime.
KMP Splash bridges this gap. It automates the generation of native splash assets and provides a Compose-ready transition layer, ensuring your app feels premium from the very first pixel.
- Single Source of Truth: Configure your background color and logo once in
build.gradle.kts. - Native Integration: Generates real
Assets.xcassetsfor iOS andthemes.xmlfor Android. - Seamless Transitions: Provides a
SplashConfigcomposable to prevent the flicker when shifting from native boot to Compose UI. - No Xcode Required: Patches
.pbxprojandInfo.plistautomatically. No more Storyboards. - Dark Mode Ready: Built-in support for dark mode background colors on both Android and iOS.
- Kotlin 2.1.0+
- Compose Multiplatform 1.7.0+
- Android:
androidx.core:core-splashscreen1.2.0+ - iOS: Xcode 14+ (uses
UILaunchScreenplist key)
Ensure you have mavenCentral() in your settings.gradle.kts:
pluginManagement {
repositories {
google()
gradlePluginPortal()
mavenCentral()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}In your gradle/libs.versions.toml:
[versions]
kmpSplash = "0.2.1"
[libraries]
kmpSplash-runtime = { module = "io.github.kmpbits:splash-runtime", version.ref = "kmpSplash" }
[plugins]
kmpSplash = { id = "io.github.kmpbits.splash", version.ref = "kmpSplash" }In your Compose App module build.gradle.kts:
plugins {
alias(libs.plugins.kmpSplash)
}
splashScreen {
backgroundColor = SplashColor.hex("#FFFFFF") // Light mode background
backgroundColorNight = SplashColor.hex("#1A1A2E") // Optional: dark mode background
logo = SplashLogo.resource("logo.png") // File in composeResources/drawable/
logoDark = SplashLogo.resource("logo_dark.png") // Optional: dark mode logo
iosProjectPath = "iosApp/iosApp" // Optional: defaults to "iosApp/iosApp"
}SplashColor.hex("#FFFFFF") // Hex string — accepts both #RRGGBB and RRGGBB
SplashColor.rgb(255, 255, 255) // RGB values (0–255 each)
SplashColor.white // Named constant
SplashColor.black // Named constantSplashLogo.resource("logo.png") // File in composeResources/drawable/
SplashLogo.path("src/commonMain/composeResources/drawable/logo.png") // Custom path relative to module
iosProjectPathshould point to the inner folder that containsInfo.plistandAssets.xcassets— typicallyiosApp/iosApp, not the rootiosAppfolder.
In your Compose App module build.gradle.kts:
commonMain.dependencies {
implementation(libs.kmpSplash.runtime)
}
androidMain.dependencies {
implementation("androidx.core:core-splashscreen:1.2.0")
}KMP Splash is integrated into the Gradle build process. On both Android and iOS, the splash assets are generated automatically when you build or run your app.
If you ever want to trigger the generation manually, you can run:
# Generate iOS assets (Info.plist, pbxproj, xcassets)
./gradlew generateLaunchScreen
# Generate Android assets (themes.xml, logo)
./gradlew generateAndroidSplashImportant
iOS Simulator Caching: iOS heavily caches the launch screen. If you change the background color or logo and don't see the changes in the simulator, you must restart the simulator (or sometimes even delete and reinstall the app) for the new assets to be reflected.
Extend SplashActivity in your MainActivity:
class MainActivity : SplashActivity() {
override suspend fun isReady(): Boolean {
delay(1000) // Load data, check auth, etc.
return true
}
override fun onFinished() {
setContent {
App()
}
}
}Call SplashConfig in your MainViewController:
fun MainViewController() = ComposeUIViewController {
var isAppReady by remember { mutableStateOf(false) }
if (!isAppReady) {
SplashConfig(
isReady = {
delay(1500) // Your initialization logic
true
},
onFinished = { isAppReady = true }
)
} else {
App()
}
}Colors and logo are picked up automatically from your Gradle configuration — no extra parameters needed.
| Platform | Native (Booting) | Compose (Loading) |
|---|---|---|
| Android | Generates themes.xml and values-night. Uses installSplashScreen(). |
Controlled by SplashActivity. |
| iOS | Patches Info.plist with UILaunchScreen, generates SplashBackground color asset and logo imageset in Assets.xcassets. |
SplashConfig uses isSystemInDarkTheme() to match the native screen exactly. |
When a KMP app starts on iOS, the OS displays the native launch screen immediately. Once the Kotlin runtime and Compose initialize (which can take 500ms+), the screen usually flashes white or black before your first Composable is rendered.
KMP Splash ensures the SplashConfig composable is visually identical to the native launch screen, providing seamless continuity that keeps your branding on screen until the app is actually ready.
App-level dark mode overrides
If your app has its own appearance setting (e.g. a dark mode toggle independent of the system setting), the native splash screen will not respect it. Both iOS UILaunchScreen and Android's SplashScreen API are rendered by the OS before any app code runs, they read the system dark mode setting directly. There is no way for any library to work around this.
The Compose layer (SplashConfig / SplashActivity) does run app code, so it can respond to your app's own preference. For the native layer, the options are:
- Use a single background color that works in both light and dark modes
- Accept the brief mismatch, the native splash shows the system color, and the Compose layer immediately corrects to your app's preferred color
This is a system limitation, not a bug in the library.
Contributions are welcome! If you find a bug or have a feature request, please open an issue or a pull request.
Copyright 2026 KMP Bits
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0