DEV Community

Cover image for Splash Screen and App Icon in React Native: Get it done right
Omotola Odumosu
Omotola Odumosu

Posted on

Splash Screen and App Icon in React Native: Get it done right

A splash screen is the first thing people see when they open your app. It usually shows your logo, app name, or a short branded animation while the app is launching. Get it right and it feels polished. Get it wrong and users notice the delay.

A lot of developers build a splash screen by creating a full React Native screen and using setTimeout to navigate away after one or two seconds. It works, but it adds extra wait time. If the app takes one second to load and your splash screen waits another second, the user is now waiting two seconds before they can do anything. That extra second is not free. It hurts performance and the user experience.

This article shows how to do it properly using the native splash screen APIs. I will cover three approaches, so you can pick the one that fits your project:

  1. Android SplashScreen API - bare React Native CLI workflow, no extra libraries.
  2. react-native-bootsplash - bare React Native CLI workflow, cross-platform and consistent.
  3. Expo app.json + expo-splash-screen - Expo workflow.

Approach 1: Using the Android SplashScreen API (bare React Native CLI)

This is the native Android approach. It uses the official androidx.core:core-splashscreen library and requires no JavaScript splash-screen component. The splash is shown by the system, not by React Native, so it appears immediately when the user taps the app icon.

Part 1 - App Icon

Prepare a square source image (clean icon asset), ideally 1024×1024 px for your Android adaptive icons(the icon will mostly be your logo and it should be a transparent background). Pick a solid background color separately (e.g "#ffffff" which is white).

Generate the icons with Android Studio:

  1. Open the android/ folder in Android Studio.
  2. In the Project panel, switch to Android view (just incase it isn't in "Android" view already but mostly when you open the android folder in Android Studio, it will already be in android view) then let android studio finish loading your project.
  3. Right-click app → res → New → Image Asset.
  4. Select Launcher Icons (Adaptive and Legacy).
  5. Under Foreground, select your logo image (transparent background).
  6. Under Background, switch from Image to Color and enter your color, for example #0aad9c.
  7. Click Next → Finish.

This creates the required icon files:
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
android/app/src/main/res/drawable/ic_launcher_foreground.xml
android/app/src/main/res/values/ic_launcher_background.xml

Now open android/app/src/main/AndroidManifest.xml and confirm it points to the generated icons:

<application
  android:icon="@mipmap/ic_launcher"
  android:roundIcon="@mipmap/ic_launcher_round"
  ... >
Enter fullscreen mode Exit fullscreen mode

If Android Studio did not generate round icons, remove the android:roundIcon line.

Part 2 - Splash Screen

Step 1 - Add the dependency

Open android/app/build.gradle and add the SplashScreen dependency androidx.core:core-splashscreen:1.0.1. your file should look like below after adding it:

dependencies {
    implementation("com.facebook.react:react-android")

    if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }

    implementation("androidx.core:core-splashscreen:1.0.1")
}
Enter fullscreen mode Exit fullscreen mode

Step 2 - Create the splash background color

Open or create android/app/src/main/res/values/colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="splash_background">#0aad9c</color>
</resources>
Enter fullscreen mode Exit fullscreen mode

Whatever color you put here becomes the splash screen background.

Step 3 - Create the splash icon drawable

You can use a PNG or a vector. Pick one option.

Option A - PNG with transparent background

Place your transparent PNG you want to use as you splash logo at android/app/src/main/res/drawable/splash_logo.png. Then create android/app/src/main/res/drawable/splash_icon.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:width="192dp"
        android:height="192dp"
        android:gravity="center">
        <bitmap android:src="@drawable/splash_logo" />
    </item>
</layer-list>
Enter fullscreen mode Exit fullscreen mode

Option B - Vector/SVG icon

The SplashScreen API works best with vectors. If your logo is an SVG:

  1. Run the project once in Android Studio so Gradle syncs.
  2. Right-click res → New → Vector Asset.
  3. Click the icon next to Clip Art and select Local file (SVG, PSD).
  4. Choose your SVG file and name it splash_logo_vector.
  5. Click Next → Finish.

This creates android/app/src/main/res/drawable/splash_logo_vector.xml, which looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="288dp"
    android:height="288dp"
    android:viewportWidth="288"
    android:viewportHeight="288">
    <path
        android:fillColor="#FFFFFF"
        android:pathData="M144,80 ..." />
</vector>
Enter fullscreen mode Exit fullscreen mode

If you use a PNG, it must be wrapped inside the layer-list as shown in Option A. Vectors can be referenced directly.

Step 4 - Add the splash theme

Open android/app/src/main/res/values/styles.xml then paste from the <!-- Splash screen theme --> downwards:

<resources>
    <!-- Your normal app theme -->
    <style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
        <!-- your existing theme items -->
    </style>

    <!-- Splash screen theme -->
    <style name="Theme.MyApp.Starting" parent="Theme.SplashScreen">
        <!-- Background color behind the icon -->
        <item name="windowSplashScreenBackground">@color/splash_background</item>

        <!-- The icon shown in the center -->
        <!-- For PNG use @drawable/splash_icon -->
        <!-- For vector use @drawable/splash_logo_vector -->
        <item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>

        <!-- Optional: max duration of the splash exit animation in ms -->
        <item name="windowSplashScreenAnimationDuration">1000</item>

        <!-- Theme to use after splash finishes -->
        <item name="postSplashScreenTheme">@style/AppTheme</item>
    </style>
</resources>
Enter fullscreen mode Exit fullscreen mode

Replace MyApp with your actual app name. The theme name must match exactly in AndroidManifest.xml just like we will do in step 5 below.

Step 5 - Apply the splash theme to MainActivity

Open android/app/src/main/AndroidManifest.xml:

<activity
    android:name=".MainActivity"
    android:theme="@style/Theme.MyApp.Starting"
    android:exported="true"
    ... >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
Enter fullscreen mode Exit fullscreen mode

Again, replace MyApp with your app name so the theme matches what you created in Step 4.

Step 6 - Update MainActivity.kt

Open android/app/src/main/java/com/myapp/MainActivity.kt and update it like this:

package com.myapp

import android.os.Bundle
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate

class MainActivity : ReactActivity() {

    override fun getMainComponentName(): String = "MyApp"

    override fun onCreate(savedInstanceState: Bundle?) {
        val splashScreen = installSplashScreen()
        super.onCreate(savedInstanceState)

        // Optional custom exit animation: fade out the splash icon
        // Remove this block if you don't want an animated exit
        splashScreen.setOnExitAnimationListener { splashScreenView ->
            splashScreenView.view
                .animate()
                .alpha(0f)
                .setDuration(500L)
                .withEndAction {
                    splashScreenView.remove()
                }
                .start()
        }
    }

    override fun createReactActivityDelegate(): ReactActivityDelegate =
        DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}
Enter fullscreen mode Exit fullscreen mode

Important: installSplashScreen() must be called before super.onCreate(savedInstanceState). Also replace com.myapp and "MyApp" with your actual package name (com.YourAppName) and app name.

Part 3 - iOS Splash Screen

iOS handles launch screens with a storyboard.

  1. In Xcode, create a LaunchScreen.storyboard.
  2. Add a centered UIImageView with your logo and a background view colored #0aad9c.
  3. Add constraints to keep the logo centered on all screen sizes.
  4. Open ios/MyApp/Info.plist and confirm it contains:
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
Enter fullscreen mode Exit fullscreen mode

iOS dismisses the launch screen automatically once the React Native root view is ready.

Part 4 - Build and Test

For Android:

cd android
./gradlew clean
cd ..
npx react-native run-android
Enter fullscreen mode Exit fullscreen mode

Or simply open the project in Android Studio and press the play button.

For iOS:

cd ios && pod install && cd ..
npx react-native run-ios
Enter fullscreen mode Exit fullscreen mode

If you already have the app installed, uninstall it first. Cached icons and splash screens can prevent your changes from showing.


Approach 2: Using react-native-bootsplash (bare React Native CLI)

If you want the same splash screen behavior on both Android and iOS without writing two completely separate native setups, react-native-bootsplash is the best option. It generates the native assets for you and gives you a clean JavaScript API to hide the splash when your app is ready.

Part 1 - App Icon

You need a square source image, ideally 1024×1024 px for Android adaptive icons which will be your foreground image (transparent image) and a background color.

Option A - Use an online generator like easyappicon

  1. Go to https://easyappicon.com or any similar icon generator.
  2. Upload your 1024×1024 source image.
  3. Download the generated zip.
  4. Inside the zip you will see an android folder and an ios folder.

For Android, copy everything inside the zip’s android folder into android/app/src/main/res/. You should end up with folders like:
android/app/src/main/res/mipmap-hdpi/
android/app/src/main/res/mipmap-mdpi/
android/app/src/main/res/mipmap-xhdpi/
android/app/src/main/res/mipmap-anydpi-v26/
android/app/src/main/res/values/ic_launcher_background.xml

Then confirm android/app/src/main/AndroidManifest.xml points to the icons:

<application
  android:icon="@mipmap/ic_launcher"
  android:roundIcon="@mipmap/ic_launcher_round"
  ... >
Enter fullscreen mode Exit fullscreen mode

If the generator did not create round icons, remove the android:roundIcon line.

For iOS, copy the zip’s ios/AppIcon.appiconset folder into ios/MyApp/Images.xcassets/AppIcon.appiconset/, and make sure AppIcon.appiconset/Contents.json is included.

Option B - Use Android Studio and Xcode directly

For Android:

  1. Open android/ in Android Studio.
  2. Right-click app/src/main/res → New → Image Asset.
  3. Choose Launcher Icons (Adaptive and Legacy).
  4. Select your foreground image and set the background color.
  5. Click Next → Finish.

For iOS:

  1. Open ios/MyApp.xcworkspace in Xcode.
  2. Select Assets.xcassets/AppIcon.
  3. Drag your 1024×1024 image into the App Store slot, or drag each generated size into its slot.

Part 2 - Splash Screen

Step A - Install the library

npm install react-native-bootsplash
cd ios && pod install && cd ..
Enter fullscreen mode Exit fullscreen mode

Step B - Prepare your logo

Create an assets folder at the project root:

mkdir assets
Enter fullscreen mode Exit fullscreen mode

Place your splash logo there. Use a transparent-background PNG or SVG, around 512×512 px. You should end up with assets/bootsplash-logo.png.

Step C - Generate the native splash assets

From the project root, run:

npx react-native-bootsplash generate assets/bootsplash-logo.png --platforms=android,ios --background=0aad9c --logo-width=120 --assets-output=assets/bootsplash --flavor=main
Enter fullscreen mode Exit fullscreen mode

This command creates:

  • android/app/src/main/res/drawable-*/bootsplash_logo.png
  • android/app/src/main/res/values/colors.xml
  • android/app/src/main/res/values/styles.xml
  • ios/MyApp/BootSplash.storyboard
  • ios/MyApp/Images.xcassets/BootSplashLogo-*/
  • assets/bootsplash/manifest.json and generated logo PNGs

Step D - Android native wiring

Open android/app/src/main/java/com/myapp/MainActivity.kt:

import android.os.Bundle
import com.zoontek.rnbootsplash.RNBootSplash

class MainActivity : ReactActivity() {

    override fun getMainComponentName(): String = "MyApp"

    override fun onCreate(savedInstanceState: Bundle?) {
        RNBootSplash.init(this, R.style.BootTheme)
        super.onCreate(savedInstanceState)
    }

    // ... keep the rest of the file unchanged
}
Enter fullscreen mode Exit fullscreen mode

If you are using react-native-screens >= 4.16.0, the order should be:

override fun onCreate(savedInstanceState: Bundle?) {
    supportFragmentManager.fragmentFactory = RNScreensFragmentFactory()
    RNBootSplash.init(this, R.style.BootTheme)
    super.onCreate(savedInstanceState)
}
Enter fullscreen mode Exit fullscreen mode

If you are using react-native-screens < 4.16.0:

override fun onCreate(savedInstanceState: Bundle?) {
    RNBootSplash.init(this, R.style.BootTheme)
    super.onCreate(null)
}
Enter fullscreen mode Exit fullscreen mode

The generator should already have set the activity theme in AndroidManifest.xml:

<activity
    android:name=".MainActivity"
    android:theme="@style/BootTheme"
    ... >
Enter fullscreen mode Exit fullscreen mode

If it did not, add android:theme="@style/BootTheme" to the activity tag.

Step E - iOS native wiring

Open ios/MyApp/AppDelegate.swift and add the import:

import RNBootSplash
Enter fullscreen mode Exit fullscreen mode

Then add this override inside the ReactNativeDelegate class:

class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {

    // ... keep sourceURL and bundleURL as they are

    override func customize(_ rootView: RCTRootView) {
        super.customize(rootView)
        RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)
    }
}
Enter fullscreen mode Exit fullscreen mode

The generator should already have updated Info.plist:

<key>UILaunchStoryboardName</key>
<string>BootSplash</string>
Enter fullscreen mode Exit fullscreen mode

If it did not, add it manually.

For older React Native templates that do not use ReactNativeDelegate or customize, call this inside didFinishLaunchingWithOptions right after creating the RCTRootView:
RNBootSplash.initWithStoryboard("BootSplash", rootView: rootView)

Step F - JavaScript wiring

In your root component, usually App.tsx or App.js:

import BootSplash from 'react-native-bootsplash';

useEffect(() => {
    const init = async () => {
        // do your startup logic here: load fonts, check auth, etc.
    };

    init().finally(async () => {
        await BootSplash.hide({ fade: true });
    });
}, []);
Enter fullscreen mode Exit fullscreen mode

Or if you use React Navigation, you can hide it when the navigator is ready:

<NavigationContainer onReady={() => BootSplash.hide({ fade: true })}>
    ...
</NavigationContainer>
Enter fullscreen mode Exit fullscreen mode

Part 3 - Build and Test

For Android:

cd android
./gradlew clean
cd ..
npx react-native run-android
Enter fullscreen mode Exit fullscreen mode

For iOS:

cd ios && pod install && cd ..
npx react-native run-ios
Enter fullscreen mode Exit fullscreen mode

If the app was already installed, uninstall it first or run ./gradlew build from Android Studio. Old splash and icon assets can be cached and will not update until you reinstall.

Part 4 - Troubleshooting

Problem Fix
Splash logo has a white background Replace assets/bootsplash-logo.png with a transparent PNG and rerun the generator.
App icon does not change Uninstall the app and run ./gradlew clean or pod install again.
Splash never hides Make sure BootSplash.hide() is actually called in your root component.
White flash before splash on Android 12+ Normal if the logo is too large; regenerate with --logo-width ≤ 120.

Approach 3: Using Expo and EAS

Expo handles most of the native configuration for you through app.json and plugins. This is the cleanest workflow if you started your project with Expo.

Part 1 - App Icon

You need a square source image, ideally 1024×1024 px for Android adaptive icons which will be your foreground image (transparent image) and a background color.

Place your icons in the project root or in an assets/ folder:

  • assets/icon.png - main app icon, 1024×1024 px
  • assets/adaptive-icon.png - Android adaptive foreground, must have transparency

Open app.json and add or update this config:

{
    "expo": {
        "icon": "./assets/icon.png",
        "android": {
            "adaptiveIcon": {
                "foregroundImage": "./assets/adaptive-icon.png",
                "backgroundColor": "#ffffff"
            }
        },
        "ios": {
            "icon": "./assets/icon.png"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Then regenerate the native projects:

npx expo prebuild --clean
Enter fullscreen mode Exit fullscreen mode

Run the app:

npx expo run:android
npx expo run:ios
Enter fullscreen mode Exit fullscreen mode

Or build with EAS:

eas build --platform android
eas build --platform ios
Enter fullscreen mode Exit fullscreen mode

Part 2 - Splash Screen

Expo gives you two main choices:

  • expo-splash-screen - works in Expo Go, official plugin, simplest setup.
  • react-native-bootsplash - more control, same library as bare React Native, but requires a development build.

This article focuses on expo-splash-screen.

Step 1 - Install the package

npx expo install expo-splash-screen
Enter fullscreen mode Exit fullscreen mode

Step 2 - Create your splash icon

Create assets/splash-icon.png with a transparent background. A size of 1280×1280 px or larger is recommended.

Step 3 - Configure the plugin in app.json

{
    "expo": {
        "plugins": [
            [
                "expo-splash-screen",
                {
                    "image": "./assets/splash-icon.png",
                    "imageWidth": 200,
                    "resizeMode": "contain",
                    "backgroundColor": "#0aad9c",
                    "dark": {
                        "image": "./assets/splash-icon-dark.png",
                        "backgroundColor": "#000000"
                    }
                }
            ]
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4 - Control when the splash hides

In your root App.tsx:

import { useCallback } from 'react';
import { View } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';

SplashScreen.preventAutoHideAsync();

export default function App() {
    const onLayoutRootView = useCallback(async () => {
        await SplashScreen.hideAsync();
    }, []);

    return (
        <View onLayout={onLayoutRootView} style={{ flex: 1 }}>
            {/* your app content */}
        </View>
    );
}
Enter fullscreen mode Exit fullscreen mode

Or with useEffect:

import { useEffect } from 'react';
import { View } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';

SplashScreen.preventAutoHideAsync();

export default function App() {
    useEffect(() => {
        const init = async () => {
            // load fonts, auth state, etc.
        };

        init().finally(() => SplashScreen.hideAsync());
    }, []);

    return (
        <View style={{ flex: 1 }}>
            {/* your app content */}
        </View>
    );
}
Enter fullscreen mode Exit fullscreen mode

Step 5 - Regenerate native code and run

npx expo prebuild --clean
npx expo run:android // for android
npx expo run:ios // for ios
Enter fullscreen mode Exit fullscreen mode

Part 3 - Troubleshooting

Problem Fix
Icon does not change Uninstall the app, run npx expo prebuild --clean, then rebuild.
Splash logo has a white background Use a PNG with real transparency, not a white-filled image.
expo-splash-screen does not hide Make sure SplashScreen.hideAsync() is called and preventAutoHideAsync() is set.
Native changes not applied Run npx expo prebuild --clean again.

Which approach should you use?

Approach Best for Works in Expo Go?
Android SplashScreen API Bare React Native, zero extra dependencies/package. Splash exits as soon as the app view loads. No
react-native-bootsplash Bare React Native, same splash on Android and iOS, and you want to control when the splash screen leaves the screen probably after user info has finished fetching, etc. No
expo-splash-screen Expo projects, quick setup. Yes

If you want full control, do not mind touching native files, and want the splash screen to disappear as soon as the app is ready, use Approach 1. If you want cross-platform consistency with a simple JavaScript hide API which allows you to keep splash screen on the screen till the users data is available to be displayed, use Approach 2. If you are using Expo and want the fastest path, use Approach 3.

Final notes

Stop adding artificial delay to your splash screen. The system splash screen exists so the user sees something immediately while your app initializes. Let it do its job, then hide it as soon as your app is ready.

Pick the approach that matches your workflow, follow the steps in order, and test on a real device when possible. Emulators are useful, but splash screen timing and icon rendering behave differently on actual hardware.

If you found this useful, share what your splash screen setup looks like in the comments. And if something here saved you from a splash screen headache, let me know.

Did this post help simplify things for you?

If yes, drop a ❤️ or 🦄 reaction and follow me here on dev.to. I share more practical, plain-English breakdowns like this.

You can also connect with me on social media. I’d love to learn, share, and grow together with you!

LinkedIn: LinkedIn
Twitter: Twitter
Instagram: Instagram
Graphics Credit: Sandra Boicheva, AppSamurai

Top comments (0)