Shipnative
ShipnativeShipnative
Sign in
GuideAugust 2026 · 7 min read

Expo Splash Screen: Setup, Sizing, and the White Flash

The splash screen is a two-second impression that a lot of otherwise good apps get wrong — a logo that looks enormous on a small phone, a blinding white background in dark mode, or a flash of nothing between the splash and the first real screen. All three are configuration, not design. Here is the current setup: the expo-splash-screen config plugin, sizing that survives every device, a dark variant, and the two API calls that make the handoff into your app seamless.

Step 1 — One image, lots of padding

You are not exporting a set of device-sized backgrounds. Expo takes one source image and centres it on a solid colour, so what you want is:

  • 1024×1024 PNG, transparent background. Square keeps the maths simple across portrait and landscape.
  • Logo mark only, centred, with padding. Roughly two-thirds artwork and one-third empty margin. That margin is what stops the logo crowding the edges on a small device.
  • No text you care about reading. A tagline that is legible in your design tool is unreadable at splash scale on a phone.

Save it as ./assets/splash-icon.png. If you also want a dark version, export the same mark in light ink as ./assets/splash-icon-dark.png.

Step 2 — The config plugin

If you have seen an older tutorial, it probably told you to use a top-level expo.splash key. That still resolves, but the plugin is the supported path and the only one with the newer options:

{
  "expo": {
    "userInterfaceStyle": "automatic",
    "plugins": [
      [
        "expo-splash-screen",
        {
          "image": "./assets/splash-icon.png",
          "imageWidth": 200,
          "resizeMode": "contain",
          "backgroundColor": "#ffffff",
          "dark": {
            "image": "./assets/splash-icon-dark.png",
            "backgroundColor": "#1c1c1c"
          }
        }
      ]
    ]
  }
}

The three options that matter:

  • imageWidth — the rendered width in points, not the file size. 200 reads as confident on a phone; past ~300 the logo starts to feel like a mistake. This is the knob people forget exists, then compensate by re-exporting the PNG at odd sizes.
  • backgroundColor— should be the background of your app’s first screen, not white-by-default. Matching them is what turns the handoff from a flash into a fade.
  • dark — without it, dark-mode users get a white rectangle followed by a dark app. It costs one extra export.

Changing this block changes native project files, so it takes effect on the next build — npx expo prebuild --clean locally, or a fresh EAS build. Reloading the JS bundle will not do it.

Step 3 — Kill the white flash

This is the part almost everyone skips. By default the splash hides the moment the first React view mounts — which is before your fonts load, before you know whether the user is signed in, and before any data arrives. The result is splash → blank screen → content. Hold the splash yourself:

// app/_layout.tsx
import { useEffect } from 'react';
import { Stack } from 'expo-router';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { useSession } from '../lib/session';

// module scope — runs before the first render
SplashScreen.preventAutoHideAsync();

export default function RootLayout() {
  const [fontsLoaded, fontError] = useFonts({
    Inter: require('../assets/fonts/Inter.ttf'),
  });
  const { isLoading: sessionLoading } = useSession();

  const ready = (fontsLoaded || fontError) && !sessionLoading;

  useEffect(() => {
    if (ready) SplashScreen.hideAsync();
  }, [ready]);

  if (!ready) return null;   // splash is still up — render nothing

  return <Stack screenOptions={{ headerShown: false }} />;
}

Two things worth pointing at. fontError is in the ready condition on purpose: if a font fails to load and you only gate on fontsLoaded, the splash never hides and the app looks frozen at launch — a bug that is invisible in development and reported as “the app won’t open” in review. And return null is what you want here, not a loading spinner: the splash is already on screen, so anything you render is hidden behind it anyway.

Recent versions of expo-splash-screen also expose SplashScreen.setOptions for a fade-out instead of a hard cut. It is a nice touch, but the ordering above is what removes the flash — the fade only polishes it.

Why it looks wrong in Expo Go

Expo Go shows its own launch screen. Your plugin config is not broken — it simply is not part of that app. To see the real thing you need a development build or a production build, which is the same reason camera, notifications, and other native config need a rebuild. The Expo Go vs development build breakdown covers where that line falls for everything else.

A 60-second checklist

  1. Square 1024×1024 transparent PNG, logo centred with padding.
  2. expo-splash-screen plugin in app.json, imageWidth around 200.
  3. backgroundColor matches your first screen — in both themes.
  4. preventAutoHideAsync() at module scope, hideAsync() after fonts and session resolve.
  5. Font-error case included in the ready condition, so a bad font can never freeze the splash.
  6. Checked on a real development build, in light and dark, on the smallest phone you support.

Or skip the config

Launch configuration is the kind of work that has exactly one correct answer and no creative upside. ShipNative generates the plugin block, the hide-when-ready wiring, and a splash background drawn from the same theme as the app it built — then previews it on your phone so you can see the actual first two seconds instead of imagining them. Everything exports as a normal Expo project, so you can still hand-tune every value above.

Frequently Asked Questions

What size should an Expo splash screen image be?

Export a single square PNG at 1024×1024 with your logo centred and generous transparent padding, then control how big it appears with imageWidth (200 is a good starting point). Because the plugin scales one source image across every device, the padding is what stops your logo touching the edges on a small phone.

Why is my splash screen white for a moment before the app loads?

The splash hides as soon as the first React view mounts, which is usually before your fonts, session check, or first data fetch finishes — so you get a flash of empty screen. Call SplashScreen.preventAutoHideAsync() at module scope, then hideAsync() only once that work is done.

Do I still use the "splash" key in app.json?

The legacy expo.splash key still resolves in current SDKs, but the expo-splash-screen config plugin is the supported path and the only one that exposes the newer options like per-theme images. If you are setting this up today, use the plugin.

Does the splash screen show up in Expo Go?

Not your custom one. Expo Go renders its own launch experience, so you have to make a development build or a production build to see your real splash screen. Testing it in Expo Go and concluding the config is broken is a very common false alarm.

How do I make the splash screen respect dark mode?

The expo-splash-screen plugin takes a dark object alongside the default options, with its own image and backgroundColor. Set userInterfaceStyle to "automatic" in app.json so the system theme is honoured, and pick a background that matches your app's first screen in each theme.

Can an AI app builder set this up for me?

Yes. ShipNative generates the app.json plugin block, the preventAutoHideAsync/hideAsync pair in the root layout, and a splash background that matches the theme it generated — so the first launch looks intentional rather than like a white flash and a logo jump.

→

App Icon Design for Indie Founders

The other first impression — and the asset your splash should match.

Read guide →
→

Expo Go vs Development Build

Why your custom splash only appears in one of them.

Read guide →

Ship a real React Native app today

Describe, preview, and export Expo code — free to start.

Build with ShipNative →
ShipNative logoShipnative

Build mobile apps with AI. Describe, preview, and ship to iOS & Android in minutes.

Features

Text to App AIApp Generator from ScreenshotPRD to Mobile App

Tools

All free toolsApp Cost CalculatorApp Name GeneratorApp Store Keyword ToolReact Native Components

Blog

All blog postsHow to Build an App Without CodingBest AI Tools for Real Mobile AppsExpo EAS App Store ChecklistLovable, Cursor & v0 for MobileBest AI App Builders in 2026React Native AI App Builder Guide

Legal

FAQTerms of ServicePrivacy Policy

© 2026 ShipNative. All rights reserved.