Skip to content

Splash screen

@symbiote-native/splash-screen wraps react-native-bootsplash so every SymbioteNative adapter can hide the native launch screen and drive its fade-out — without importing that library’s React hook body. Unlike the slider wrapper, bootsplash has no native view to register: it exposes only an imperative TurboModule (hide/isVisible/getConstants()), so there is nothing for the engine’s ViewConfig path to reach — every adapter calls straight into the same native module.

OS platform Support
iOS ✅ live
Android ✅ live
Framework adapter Support
React ✅ live
Vue ✅ live
Angular ✅ live
Svelte ✅ live
Terminal window
npm install @symbiote-native/splash-screen

@symbiote-native/splash-screen itself is a workspace package (packages/splash-screen), not yet published — add it as a workspace dependency the same way the examples do. It ships as the sole autolinked native proxy for react-native-bootsplash (react-native.config.cjs + symbiote-splash-screen.podspec), so an app never lists react-native-bootsplash directly.

Once the native launch screen is wired up and shown at boot, call hide() after your JS tree has mounted. hide()/isVisible() carry zero framework dependency — they are the same react-native-bootsplash functions, re-exported verbatim by every adapter.

import { useEffect } from 'react';
import { hide } from '@symbiote-native/splash-screen/react';
export default function App() {
useEffect(() => {
hide();
}, []);
// ...
}
import { useState } from 'react';
import { View, Image, Animated } from '@symbiote-native/react';
import { useHideAnimation } from '@symbiote-native/splash-screen/react';
import manifest from '../assets/bootsplash/manifest.json';
import logo from '../assets/bootsplash/logo.png';
export default function AnimatedSplash() {
const [opacity] = useState(() => new Animated.Value(1));
const [ready, setReady] = useState(false);
const { container, logo: logoProps } = useHideAnimation({
manifest,
logo,
ready,
animate: () => {
Animated.timing(opacity, { toValue: 0, duration: 250, useNativeDriver: true }).start();
},
});
return (
<Animated.View {...container} style={[container.style, { opacity }]}>
<Image {...logoProps} />
</Animated.View>
);
}
Signature Description
hide(config?: { fade?: boolean }): Promise<void> Hides the native splash screen. fade: true fades the native view out before removing it; omitted/false removes it immediately — independent of the JS-side useHideAnimation fade below
isVisible(): boolean Reads whether the native splash screen is currently shown. Synchronous — iOS exports it as a blocking sync method, Android returns a plain boolean
Field Type Default Description
manifest IManifest (required) The generated assets/bootsplash/manifest.json — background color plus logo size, optionally a dark background and/or brand
logo / darkLogo IImageSourceProp The logo image to fade in; omit to skip the logo readiness gate entirely
brand / darkBrand IImageSourceProp A secondary “powered by”-style image below the logo; only read when manifest.brand is present
ready boolean true Your own extra readiness gate (e.g. “auth check done”) — animate waits for this to be true too
animate () => void (required) Called exactly once, the moment every readiness condition (layout committed + images loaded + ready) is met — put your own fade-out here
statusBarTranslucent boolean false Android-only: skip the extra top margin compensation when your app already draws a translucent status bar
navigationBarTranslucent boolean false Android-only: skip the extra bottom margin compensation when your app already draws a translucent navigation bar
Field Type Description
container { style, onLayout } Bind onto your own Viewstyle positions and colors the full-screen backdrop, onLayout reports layout readiness
logo { source, fadeDuration, resizeMode, style, onLoadEnd } Bind onto your own ImageonLoadEnd reports logo-load readiness
brand { source, fadeDuration, resizeMode, style, onLoadEnd } Same shape as logo, bound onto a second Image for the optional brand asset
  • Calling hide() on your very first render is safe — it queues rather than failing. iOS holds the resolver until a 0.35 s timer started during native init reports that the system launch screen has faded out; Android re-posts the request every 100 ms while the module is still initializing or no activity is resumed. The promise settles late; it does not reject.
  • useHideAnimation hides the native screen without a fade. It calls hide({ fade: false }) itself the moment the readiness gate closes, because your animate() owns the fade-out — hide({ fade: true }) only matters when you hide the splash by hand.
  • animate() runs at most once per mount. The controller latches after the first call, and the logo/brand readiness flags are captured when it is constructed — a config that later drops its logo/brand source does not flip them back. If ready never becomes true, or a readiness callback is never wired, hide() never fires and the splash stays up.
  • Native constants are read once, on first render. darkModeEnabled is what picks darkBackground/darkLogo/darkBrand, so switching the system theme while the splash is still on screen does not re-pick the dark assets.
  • Only Android reports layout constants. iOS exports darkModeEnabled alone; statusBarHeight, navigationBarHeight and logoSizeRatio are Android-only, which is why the container’s negative margins and the logo scaling apply there and nowhere else. On Samsung One UI 4 devices logoSizeRatio is 0.5, so the logo renders at half its manifest size by design.

@symbiote-native/splash-screen ships zero native metadata of its own — the native launch screen is generated once by the bundled symbiote-splash-screen CLI (a thin passthrough to react-native-bootsplash’s own generator) and wired into the app’s native entry points by hand, see the native-setup how-to. The package’s own code is a pure JS bridge:

packages/splash-screen/src/
├── core/ # framework-agnostic: hide/isVisible re-export, getHideAnimationConstants
│ # (reads RNBootSplash's TurboModule via getEnforcingNativeModule),
│ # HideAnimationController (readiness state machine), computeHideAnimationStyles
├── react/ # React lifecycle (hooks) over the core
├── vue/ # Vue lifecycle (composables) over the core
├── angular/ # Angular lifecycle (a DI service) over the core
└── svelte/ # Svelte lifecycle (runes/use-hide-animation.svelte.ts) over the core

Every adapter’s useHideAnimation constructs the same HideAnimationController once, reads native constants once, and re-syncs the controller’s config through its own reactivity primitive — a React effect with no dependency array, a Vue watchEffect, an Angular effect(), a Svelte $effect. The style computation itself (computeHideAnimationStyles) is a faithful, framework-agnostic port of react-native-bootsplash’s own useHideAnimation useMemo body, so the container/logo/brand prop bags are byte-for-byte the same shape upstream produces — the same logic/lifecycle split as every other SymbioteNative component (see how it works).