One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Launch Screen

Hold the native launch screen until your app is ready

One.LaunchScreen holds the native launch screen past first content until your startup work finishes: fonts, session, initial data. Prebuild keeps the iOS storyboard and the Android system splash visible until React Native draws; an app that reveals itself later stops that automatic release and hides the screen itself.

On iOS hiding removes the launch view, with an optional fade. On Android 12 and later, holding the screen with preventAutoHide also hands the splash’s exit to One: hide lifts it at once or fades it, instead of playing the system’s own animation. On web there is no launch screen and the calls do nothing.

Usage

import { One } from 'one'
One.LaunchScreen.preventAutoHide()
async function loadInitialData() {}
async function startApp() {
try {
await loadInitialData()
} finally {
One.LaunchScreen.hide({ fade: true })
}
}
void startApp()

Call preventAutoHide at module evaluation time, before the first render. Call hide once the app can paint its real first screen.

How it works

Both methods are synchronous. The hold applies to the current launch only, so call preventAutoHide on each cold start that needs it. If startup never prevents, the screen lifts with first content and hide has nothing to do. hide stays safe to call again after the screen is gone, which makes finally blocks and double-invoked effects harmless.

fade: true fades the launch screen out instead of removing it at once. Android versions below 12 draw the launch screen as the window background, which goes away at once.

Animate from the launch screen

One.LaunchScreen.Cover draws the configured launch screen in React Native, exactly where the native one sits: the ground colour, the background image, and the artwork at its configured width, centered, with the dark variants. It reads the launch resources prebuild already bundled into the app, so nothing is configured twice. Mount it above your first screen, animate it, and call hide once it is drawn: the native screen lifts away under it with no visible change.

import { useEffect, useState } from 'react'
import Animated, { FadeOut } from 'react-native-reanimated'
import { One } from 'one'
One.LaunchScreen.preventAutoHide()
export function Root({ children }: { children: React.ReactNode }) {
const [covered, setCovered] = useState(true)
useEffect(() => {
One.LaunchScreen.hide()
const timer = setTimeout(() => setCovered(false), 400)
return () => clearTimeout(timer)
}, [])
return (
<>
{children}
{covered && (
<Animated.View exiting={FadeOut} style={{ position: 'absolute', inset: 0 }}>
<One.LaunchScreen.Cover />
</Animated.View>
)}
</>
)
}

Pass artwork={false} to draw only the ground and background image, and put your own animated artwork in children. One.LaunchScreen.artworkWidth is the configured width in points (null without native.app.splash); the artwork is centered on the window. The cover is built from the resources of the last prebuild, so rebuild the native app after changing the splash. On web it renders nothing.

API reference

MethodsTypeDescription
preventAutoHide() => voidHold the screen past first content.
() => voidHold the screen past first content.
hide(options?: LaunchScreenHideOptions) => voidHide the screen. Safe to repeat.
(options?: LaunchScreenHideOptions) => voidHide the screen. Safe to repeat.
CoverComponentType<LaunchScreenCoverProps>The configured launch screen drawn in React Native.
ComponentType<LaunchScreenCoverProps>The configured launch screen drawn in React Native.
artworkWidthnumber | nullThe configured artwork width in points.
number | nullThe configured artwork width in points.
Cover propsTypeDescription
artworkbooleanDraw the artwork. Default true.
booleanDraw the artwork. Default true.
childrenReactNodeDrawn over the launch screen.
ReactNodeDrawn over the launch screen.
Hide optionsTypeDescription
fadebooleanFade out instead of removing at once.
booleanFade out instead of removing at once.

Platform differences

iOSAndroidWeb
ScreenStoryboardSystem splashNone
HoldPast contentPast contentNo effect
FadeOptionalAndroid 12+No effect
CoverDrawsDrawsNothing
Repeat hideSafeSafeSafe

Limits and gotchas

Examples

Gate on fonts and session

import { useEffect, useState } from 'react'
import { One } from 'one'
One.LaunchScreen.preventAutoHide()
function Gate({ children }: { children: React.ReactNode }) {
const [ready, setReady] = useState(false)
useEffect(() => {
async function start() {
try {
await Promise.all([loadFonts(), loadSession()])
} finally {
setReady(true)
One.LaunchScreen.hide({ fade: true })
}
}
void start()
}, [])
if (!ready) return null
return <>{children}</>
}
async function loadFonts() {}
async function loadSession() {}

Preventing at module scope wins the race with first content. The gate renders nothing until startup settles, then reveals app and content together.

Hide after the first frame

import { useEffect } from 'react'
import { One } from 'one'
function useHideOnMount() {
useEffect(() => {
One.LaunchScreen.hide()
}, [])
}

Screens with no startup work still benefit from an explicit hide in the root effect: it documents the handoff and stays harmless where nothing was held.

Edit this page on GitHub.