How to Add a Splash Screen to Your Expo App

DavidDavid September 17, 2026 8 min read
A smartphone lying face-up on a dark walnut desk, its screen glowing a deep solid indigo with a single small centered emblem of light, beside a brass drafting compass and a folded charcoal cloth in low warm evening light
Original image, App Store Launch Club

Adding a Splash Screen to Your Expo App: the Short Answer

Adding a splash screen to your Expo app is three steps: install the expo-splash-screen package, configure its plugin in app.json with your logo image and a background color, and make a fresh build so the native splash gets baked in. That covers what shows. The second half is when it hides - by default the splash disappears the moment JavaScript loads, which is before your fonts and data are ready, so you call preventAutoHideAsync at startup and hideAsync once your first screen has rendered. Get both halves right and cold start looks deliberate instead of flickery.

The splash screen is native, not JavaScript. It is drawn by the operating system before your React code runs at all, which is exactly why it exists - it covers the gap between the user tapping your icon and your first screen being ready. That has two practical consequences. Changes to the splash require a new build, not a reload. And the hand-off from splash to first screen is the part users actually notice, so most of this guide is about making that hand-off seamless.

Step 1 - Install expo-splash-screen and Configure the Plugin

Install the package with npx expo install expo-splash-screen so the version matches your Expo SDK. Then configure the splash in app.json through the expo-splash-screen config plugin: the image (your logo, centered), an imageWidth so it renders at a sane size, a resizeMode, and the backgroundColor that fills the rest of the screen.

  1. Run npx expo install expo-splash-screen in your project so the package version is pinned to your SDK.
  2. Export your logo as a large square PNG with a transparent background - just the mark, no wordmark padding, no baked-in background color.
  3. Add expo-splash-screen to the plugins array in app.json with image, imageWidth, resizeMode contain, and backgroundColor set to the flat color you want behind the logo.
  4. If your app supports dark mode, add the dark variant in the same plugin config - a dark backgroundColor and, if your logo needs it, a separate dark image - so the splash matches the system theme instead of flashing a light screen at a dark-mode user.
  5. Make a new development build or EAS build. The splash is compiled into the native binary, so neither a JavaScript reload nor an old build will show your changes.

Step 2 - Hold the Splash Until Your App Is Actually Ready

Left alone, the native splash hides as soon as your JavaScript loads. But loaded is not ready - your fonts are still coming in, your cached session is still being read, your first screen has not rendered. The result is the classic white flash: splash disappears, a blank frame shows, then your UI pops in. The fix is two calls from the expo-splash-screen API.

  1. At module scope in your root component file, call SplashScreen.preventAutoHideAsync(). This tells the native side to keep the splash up instead of hiding it the moment JavaScript loads.
  2. Do your startup work while the splash holds: load fonts, read the stored session, fetch whatever your first screen cannot render without.
  3. When that work is done and your first screen has laid out, call SplashScreen.hideAsync(). A common pattern is calling it from an onLayout handler on your root view, so the splash drops only after real UI exists behind it.
  4. Keep the held time honest - a splash that holds for several seconds while you prefetch nice-to-have data feels broken. Load only what the first screen needs, and let the rest stream in after.

One more seam is worth closing: set your app's root background color to the same color as the splash backgroundColor. If the two match, the transition from splash to first screen reads as one continuous surface even if a frame of bare background slips through. If they differ, every cold start shows a color pop.

The First Frame Pass: Our Pre-Submission Splash Check

Inside App Store Launch Club we call this the First Frame Pass - a short check of everything the user sees between tapping your icon and touching your first screen, run on a real device from a production build before you submit. Cold start is the first impression of your app, and it is also where release-only problems hide, because development builds start differently than production ones.

  • Kill the app fully and cold start it. The splash should show your logo centered on your background color, correctly sized, on both a light-mode and a dark-mode device.
  • Watch the hand-off. No white flash, no color pop, no half-rendered frame between splash and first screen.
  • Time it by feel. The splash should hold just long enough for the first screen to be real - if you are staring at it, trim what you load at startup.
  • Run the pass on the production build, not just development. If the app dies during the splash in release only, that is a startup crash, and the walkthrough in [why your Expo app crashes on launch in a production build](/blog/expo-app-crashes-on-launch-in-production-build) traces the usual causes.

Let Claude Code Wire the Whole Thing

Splash screen setup is mechanical, which makes it a clean one-pass job for Claude Code from the desktop app. Describe the outcome - 'add a splash screen with my logo on a navy background, hold it until fonts load, no white flash' - and it installs the package, writes the plugin config, adds the preventAutoHideAsync and hideAsync calls in the right places, and aligns your root background color with the splash. The parts it cannot do are the ones that belong to you: exporting the logo asset and running the First Frame Pass on a real device.

  1. Have Claude Code install expo-splash-screen and add the plugin config to app.json, including the dark-mode variant if your app supports one.
  2. Ask it to wire the hold-and-release pattern: preventAutoHideAsync at startup, your font and session loading, hideAsync from the root view's onLayout.
  3. Tell it to set the app's root background color to match the splash backgroundColor so the hand-off is seamless.
  4. Then rebuild and run the First Frame Pass yourself on a device - cold start, light and dark, production build.

If your app has a dark theme, the splash is one of the surfaces that has to respect it - a light splash flashing before a dark app is exactly the kind of seam users clock instantly. The full theming sweep is in [how to add dark mode to your Expo app](/blog/how-to-add-dark-mode-to-your-expo-app).

Common Splash Screen Mistakes and How to Avoid Them

Splash problems cluster around a handful of predictable mistakes, and each one has a mechanical fix.

Common splash screen mistakes in an Expo app

MistakeWhat goes wrongFix
Editing the config without rebuildingThe old splash keeps showing no matter what you changeThe splash is native - make a fresh development or EAS build after any config change
Full-bleed splash artworkAndroid 12 and later crop it to a centered icon and it looks brokenDesign a centered logo on a flat backgroundColor - the layout that renders correctly on every platform
Letting the splash auto-hideWhite flash between the splash and your first screen on cold startCall preventAutoHideAsync at startup and hideAsync only after your first screen has laid out
Splash and app background colors differA visible color pop on every launchSet the root view background to the exact splash backgroundColor
No dark-mode variantDark-mode users get a bright flash before a dark appAdd the dark backgroundColor (and image if needed) in the plugin config
Holding the splash too longStartup feels broken while nice-to-have data prefetchesLoad only what the first screen needs before hideAsync; stream the rest after

TL;DR

Add a splash screen to your Expo app by installing expo-splash-screen with npx expo install, configuring the plugin in app.json with a centered transparent-background logo, an imageWidth, and a flat backgroundColor, then making a fresh build - the splash is native, so config edits show nothing until you rebuild. Control the hand-off with preventAutoHideAsync at startup and hideAsync once your first screen has laid out, and match your root background color to the splash color so cold start reads as one continuous surface. Design for Android 12's centered-icon system splash and add a dark-mode variant so no user gets a bright flash. Before you submit, run the First Frame Pass on a real device from a production build: cold start, both themes, no white flash, no color pop. Claude Code wires the package, the config, and the hold-and-release calls in one pass; you export the logo and verify the first frame on hardware.

The splash screen is one small piece of shipping an app that passes review and feels finished on day one. Inside App Store Launch Club, builders run checks like the First Frame Pass together and get their cold start, their icon, and their whole listing looked at by people who have already shipped. Join at applaunchclub.co for $9 a month and bring your build.

Free app-building tips, straight to your inbox

Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.

Frequently asked questions

Why does my Expo splash screen not update when I change app.json?

Because the splash is part of the native build, not the JavaScript bundle. The operating system draws it before your React code runs, so it is compiled into the binary at build time. After changing the expo-splash-screen plugin config, make a fresh development build or EAS build. A JavaScript reload, or reopening an existing build, will keep showing the old splash.

How do I keep the splash screen visible until my app is ready?

Call SplashScreen.preventAutoHideAsync() at module scope in your root component file, do your startup work (fonts, stored session, first-screen data), then call SplashScreen.hideAsync() once your first screen has rendered - commonly from an onLayout handler on the root view. Without this, the splash hides as soon as JavaScript loads, which produces the white flash between splash and UI.

Why does my splash screen look like a small icon on Android?

Android 12 and later use the system splash screen, which renders your image as a centered icon on a solid background color and does not honor full-screen artwork. This is platform behavior, not a bug in your config. Design for it: a simple centered logo with a flat backgroundColor renders correctly on modern Android and iOS alike.

Can I use a full-screen image as my Expo splash screen?

Not reliably. On Android 12 and later the system crops your splash to a centered icon on a solid color, so full-bleed artwork gets cut down and looks broken. The durable design is the one Expo's config encodes: a centered logo image plus a backgroundColor. Put the rich visual treatment in your first screen instead, where you control every pixel.

How do I fix the white flash after the splash screen?

Two seams cause it. First, the splash auto-hides before your first screen is ready - fix that with preventAutoHideAsync and hideAsync after your root view lays out. Second, your app's root background color differs from the splash backgroundColor, so a frame of mismatched color shows during the hand-off - set them to the same value. Close both seams and cold start is continuous.

Does the splash screen support dark mode?

Yes. The expo-splash-screen plugin config accepts a dark variant - a separate backgroundColor and, if your logo needs it, a separate image - and the system picks the right one based on the device theme. If your app has a dark theme and your splash does not, dark-mode users get a bright flash on every launch, so add the variant in the same pass as the light splash.

Last reviewed by David on September 17, 2026

David

Written by

David

Founder and app builder

Keep reading

App StoreLaunch

App Store Phased Release: How the 7-Day Rollout Works, How to Pause It, and When to Skip It

App Store phased release rolls a version update out to a random sample of users with automatic updates on over 7 days: 1 percent on day one, then 2, 5, 10, 20, 50 and 100. You can pause it for up to 30 days in total, or release to everyone with one button. Here is exactly what Apple says it does, what it does not do, and how I decide when an Expo app update should use it.

David 8 min
Read article
ExpoClaude Code

How to Add Haptic Feedback to Your Expo App

How to add haptic feedback to your Expo app: install expo-haptics, then call the right method for the moment - selectionAsync for pickers, impactAsync for taps and gestures, notificationAsync for outcomes. Here is the exact mapping, the reason the simulator stays silent, and the one rule that keeps haptics from feeling cheap.

David 8 min
Read article

Ready to build it yourself?

Join App Store Launch Club, the #1 community for building and launching apps, for $9/month.

← Back to the blog