What Are the Best Practices for Splash Screens in Jetpack Compose Apps?
The Direct Answer
The best splash screen is one users barely notice. Use the Android Splash Screen API with a simple background and centered icon, extend it only for critical initialization (not data loading), and ensure the transition to your app is seamless. Avoid complex animations, never use splash screens for branding alone, and always use the androidx library for consistent behavior across devices.
The Problem
Splash screens have a troubled history. Before Android 12, developers created custom splash screens using separate activities or overlay views, leading to inconsistent experiences across apps. The Android 12 Splash Screen API was introduced to standardize behavior, but it came with its own challenges and potential pitfalls.
A Reddit discussion on r/androiddev captured the core issue:
“Users hate meaningless splash screens. Every second your splash screen adds to startup is a second the user is waiting instead of using your app.”
The problem compounds when developers misuse the splash screen API:
What Developers Do: -> Show splash for 3+ seconds for "branding" -> Load all data during splash screen -> Create complex animated logos -> Use splash as a "loading screen"
What Users Experience: -> Frustration waiting for the app -> Perception of slow, bloated app -> Negative app store reviews -> Uninstall if it happens repeatedlyFive Essential Best Practices
Practice 1: Use the androidx Library (Always)
Even if your minSdk is 31 (Android 12+), use the androidx.core:core-splashscreen library. It provides:
- Consistent behavior across all Android versions
- Fixes for OEM-specific bugs
- Backward compatibility for older devices
- Proper handling of edge cases
dependencies { // Always use the androidx library implementation("androidx.core:core-splashscreen:1.0.1")}Practice 2: Keep It Minimal
The splash screen should be simple: a solid background color with a centered icon. No complex animations, no video, no loading indicators.
<style name="Theme.App.Splash" parent="Theme.SplashScreen"> <!-- Transition to your app theme --> <item name="postSplashScreenTheme">@style/Theme.App</item>
<!-- Simple, solid background --> <item name="windowSplashScreenBackground">@color/splash_background</item>
<!-- Centered icon (your app icon works well) --> <item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<!-- Animation duration: 200-500ms for smooth exit --> <item name="windowSplashScreenAnimationDuration">300</item></style><?xml version="1.0" encoding="utf-8"?><!-- Simple centered icon - no complex animations --><layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:width="108dp" android:height="108dp" android:drawable="@mipmap/ic_launcher_round" /></layer-list>Practice 3: Extend Only When Necessary
Use setKeepOnScreenCondition sparingly. Only extend the splash screen for:
- Critical database initialization
- Essential preference loading
- Authentication state check
- Required SDK initialization
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState)
// ONLY for critical initialization var isReady = false splashScreen.setKeepOnScreenCondition { !isReady }
lifecycleScope.launch { // Critical initialization only initializeCriticalComponents() isReady = true }
setContent { AppTheme { // Your UI } } }}Practice 4: Use OnExitAnimationListener Wisely
The exit animation listener lets you customize the transition from splash to your app. Use it sparingly.
override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState)
// Optional: Customize exit animation splashScreen.setOnExitAnimationListener { splashScreenView -> // Create custom transition splashScreenView.remove() }
setContent { AppTheme { // Your UI should match splash screen for seamless transition } }}The key principle: your app’s initial state should visually match the splash screen for a seamless transition. If the splash is white with a centered icon, your app’s first frame should look similar.
Practice 5: Test on Multiple OEMs
Device manufacturers modify Android’s splash screen behavior. Test on:
- Samsung devices (OneUI)
- Xiaomi devices (MIUI/HyperOS)
- Huawei devices (EMUI)
- OnePlus devices (OxygenOS)
- Google Pixel devices (stock Android)
Samsung: -> Sometimes ignores animation duration -> May clip splash icon
Xiaomi: -> Can show double splash with custom themes -> Different behavior in MIUI versions
Huawei: -> May override background colors -> Exit animation timing differs
OnePlus: -> Generally follows stock behavior -> Minor timing differencesCommon Mistakes to Avoid
Mistake 1: The “2x Splash” Anti-Pattern
This happens when you have both an XML splash screen AND a custom Compose splash screen:
// XML splash shows first (from theme)// Then this shows second - user sees TWO splash screens!setContent { if (isLoading) { CustomSplashScreen() // REMOVE THIS } else { MainContent() }}Solution: Remove any custom splash screen UI in Compose. Use the Android Splash Screen API only.
Mistake 2: Using Splash for Branding
<style name="Theme.App.Splash" parent="Theme.SplashScreen"> <!-- DON'T: Complex animated logo for branding --> <item name="windowSplashScreenAnimatedIcon">@drawable/animated_brand_logo</item> <item name="windowSplashScreenAnimationDuration">3000</item></style>Users don’t want to see your logo for 3 seconds. They want to use your app.
Mistake 3: Loading Data During Splash
splashScreen.setKeepOnScreenCondition { !dataLoaded }
lifecycleScope.launch { // DON'T: Load all data during splash userRepository.loadAllUsers() productRepository.loadAllProducts() orderRepository.loadOrderHistory() dataLoaded = true}This is problematic because:
- Splash code may not run on all launches
- Network failures leave users stuck on splash
- Users wait longer than necessary
Solution: Load data in your UI with loading states.
Mistake 4: Extending Splash Unnecessarily
splashScreen.setKeepOnScreenCondition { !isReady }
lifecycleScope.launch { // DON'T: These don't need to block splash analytics.initialize() crashReporting.setup() featureFlags.fetch() isReady = true}Analytics and feature flags can initialize in the background. Don’t make users wait.
Mistake 5: Ignoring Exit Animation
// Splash screen with white background// App starts with dark theme// User sees jarring flashSolution: Match your app’s initial frame to the splash screen appearance.
Best Practice Implementation Checklist
[ ] Added androidx.core:core-splashscreen dependency[ ] Created Theme.SplashScreen in XML[ ] Set postSplashScreenTheme reference[ ] Applied splash theme to launcher activity in manifest[ ] Called installSplashScreen() BEFORE super.onCreate()[ ] Used simple background + centered icon only[ ] Animation duration between 200-500ms[ ] Only extended splash for critical initialization[ ] No custom Compose splash screen UI[ ] Tested on Samsung, Xiaomi, Huawei, OnePlus devices[ ] App's first frame matches splash screen appearance[ ] No data loading during splashComplete Working Example
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { // 1. Install splash screen FIRST val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
// 2. Only extend for critical initialization var isCriticalReady = false splashScreen.setKeepOnScreenCondition { !isCriticalReady }
lifecycleScope.launch { // Critical work only Database.init(this@MainActivity) isCriticalReady = true }
// 3. Set up your Compose UI setContent { val isReady by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { // Non-critical work Analytics.init() isReady = true }
AppTheme { if (isReady) { MainContent() } else { // Loading state, NOT another splash screen LoadingScreen() } } } }}<application android:theme="@style/Theme.App"> <activity android:name=".MainActivity" android:exported="true" android:theme="@style/Theme.App.Splash"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity></application>Why This Matters
| Aspect | Impact |
|---|---|
| User Experience | Users perceive faster app startup |
| Platform Compliance | Follows Android design guidelines |
| Cold Start Perception | Seamless transition masks initialization |
| App Store Ratings | Fewer complaints about slow startup |
| Device Fragmentation | Consistent behavior across OEMs |
Summary
The best splash screen implementation is one that users barely notice. Follow these five practices:
- Use the androidx library - Ensures consistent behavior across all devices and Android versions
- Keep it minimal - Simple background + centered icon, no complex animations
- Extend only when necessary - Only for critical initialization, never for data loading
- Handle exit animations wisely - Match your app’s first frame to the splash appearance
- Test on multiple OEMs - Samsung, Xiaomi, Huawei, and others have different behaviors
The splash screen API is a tool for platform compliance and smooth perceived startup, not a marketing opportunity. Treat it that way, and your users will thank you.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 Reddit Discussion: Splash Screen Best Practices
- 👨💻 Android Splash Screen API Documentation
- 👨💻 androidx.core:splashscreen Library
- 👨💻 Material Design - Launch Screen Guidelines
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments