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:
- Android SplashScreen API - bare React Native CLI workflow, no extra libraries.
- react-native-bootsplash - bare React Native CLI workflow, cross-platform and consistent.
- 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:
- Open the android/ folder in Android Studio.
- 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.
- Right-click app → res → New → Image Asset.
- Select Launcher Icons (Adaptive and Legacy).
- Under Foreground, select your logo image (transparent background).
- Under Background, switch from Image to Color and enter your color, for example #0aad9c.
- 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"
... >
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")
}
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>
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>
Option B - Vector/SVG icon
The SplashScreen API works best with vectors. If your logo is an SVG:
- Run the project once in Android Studio so Gradle syncs.
- Right-click res → New → Vector Asset.
- Click the icon next to Clip Art and select Local file (SVG, PSD).
- Choose your SVG file and name it splash_logo_vector.
- 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>
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>
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>
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)
}
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.
- In Xcode, create a LaunchScreen.storyboard.
- Add a centered UIImageView with your logo and a background view colored #0aad9c.
- Add constraints to keep the logo centered on all screen sizes.
- Open ios/MyApp/Info.plist and confirm it contains:
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
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
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
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
- Go to https://easyappicon.com or any similar icon generator.
- Upload your 1024×1024 source image.
- Download the generated zip.
- 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"
... >
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:
- Open android/ in Android Studio.
- Right-click app/src/main/res → New → Image Asset.
- Choose Launcher Icons (Adaptive and Legacy).
- Select your foreground image and set the background color.
- Click Next → Finish.
For iOS:
- Open ios/MyApp.xcworkspace in Xcode.
- Select Assets.xcassets/AppIcon.
- 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 ..
Step B - Prepare your logo
Create an assets folder at the project root:
mkdir assets
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
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
}
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)
}
If you are using react-native-screens < 4.16.0:
override fun onCreate(savedInstanceState: Bundle?) {
RNBootSplash.init(this, R.style.BootTheme)
super.onCreate(null)
}
The generator should already have set the activity theme in AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:theme="@style/BootTheme"
... >
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
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)
}
}
The generator should already have updated Info.plist:
<key>UILaunchStoryboardName</key>
<string>BootSplash</string>
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 });
});
}, []);
Or if you use React Navigation, you can hide it when the navigator is ready:
<NavigationContainer onReady={() => BootSplash.hide({ fade: true })}>
...
</NavigationContainer>
Part 3 - Build and Test
For Android:
cd android
./gradlew clean
cd ..
npx react-native run-android
For iOS:
cd ios && pod install && cd ..
npx react-native run-ios
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"
}
}
}
Then regenerate the native projects:
npx expo prebuild --clean
Run the app:
npx expo run:android
npx expo run:ios
Or build with EAS:
eas build --platform android
eas build --platform ios
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
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"
}
}
]
]
}
}
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>
);
}
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>
);
}
Step 5 - Regenerate native code and run
npx expo prebuild --clean
npx expo run:android // for android
npx expo run:ios // for ios
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)