rn-native-splash-overlay
v0.1.1
Published
Custom splash overlay for React Native apps.
Maintainers
Readme
rn-native-splash-overlay
Custom React Native splash overlay with native performance for iOS & Android.
✨ Features
- Native splash overlay (no JS lag)
- Smooth fade animation
- Cross-platform API
- New Architecture compatible
- Easy 3-step integration
📦 Installation
yarn add rn-native-splash-overlay
or npm install rn-native-splash-overlay
Android Setup
Step 1: Add Package to MainApplication.kt
// MainApplication.kt - add to getPackages() override fun getPackages(): List = PackageList(this).packages.apply { add(SplashOverlayPackage()) // Add this line }
Step 2: Add Splash Image to Drawable
Copy your splash image to android/app/src/main/res/drawable/splash.png (all densities: mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi).
Step 3: Update styles.xml
Add LaunchTheme to android/app/src/main/res/values/styles.xml:
It will look like this below: @drawable/rn_edit_text_material
<style name="LaunchTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<!-- Android 14+ safe area handling -->
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">false</item>
<item name="android:windowLightNavigationBar">false</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>We are using @color/splash_background in our layout (see next step), also create colors.xml:
android/app/src/main/res/values/colors.xml:
Step 4: Update AndroidManifest.xml
<activity android:name=".MainActivity" android:launchMode="singleTask" android:theme="@style/LaunchTheme" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:windowSoftInputMode="adjustResize" android:exported="true">
Step 5: Create Layout File
Create android/app/src/main/res/layout/splash_overlay.xml:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/splash" />Step 6: Update MainActivity.kt
Replace your default MainActivity.kt with this version including splash logic:
package com.yourapp.package // e.g. com.overlay
import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import android.widget.FrameLayout import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.ReactRootView
class MainActivity : ReactActivity() {
var splashOverlay: FrameLayout? = null
override fun getMainComponentName(): String = "Overlay" // Your JS entry component name
override fun onCreate(savedInstanceState: Bundle?) { // Switch away from the launch theme so we are not stuck on the splash drawable. setTheme(R.style.AppTheme) super.onCreate(savedInstanceState) }
// Called from native module via reflection fun hideSplashOverlay() { android.util.Log.d("SplashOverlay", "hideSplashOverlay() start")
val overlay = splashOverlay
if (overlay == null) {
android.util.Log.d("SplashOverlay", "hideSplashOverlay(): splashOverlay is null")
return
}
val parent = overlay.parent as? ViewGroup
if (parent != null) {
parent.removeView(overlay)
android.util.Log.d("SplashOverlay", "hideSplashOverlay(): overlay removed from parent")
} else {
android.util.Log.d("SplashOverlay", "hideSplashOverlay(): parent is null")
}
splashOverlay = null}
override fun createReactActivityDelegate(): ReactActivityDelegate { return MainActivityDelegate(this, mainComponentName) }
class MainActivityDelegate( private val activity: MainActivity, mainComponentName: String ) : ReactActivityDelegate(activity, mainComponentName) {
private var rootView: ReactRootView? = null
override fun createRootView(): ReactRootView {
val view = ReactRootView(context)
rootView = view
return view
}
override fun loadApp(appKey: String?) {
android.util.Log.d("SplashOverlay", "MainActivityDelegate.loadApp() called")
super.loadApp(appKey)
val contentView = activity.findViewById<FrameLayout>(android.R.id.content)
if (contentView == null) return
if (activity.splashOverlay != null) return
val overlay = LayoutInflater.from(activity)
.inflate(R.layout.splash_overlay, contentView, false) as FrameLayout
contentView.addView(overlay)
activity.splashOverlay = overlay
}} }
If your app already uses DefaultReactActivityDelegate and New Architecture helpers, adapt this logic into your existing delegate; the key parts are:
onCreate → setTheme(R.style.AppTheme)
var splashOverlay: FrameLayout?
fun hideSplashOverlay()
loadApp() inflating R.layout.splash_overlay
Step 7: (Optional) JS root background
For best results and to avoid showing the LaunchTheme background after the overlay is hidden, make your root JS view opaque:
import { SafeAreaProvider } from 'react-native-safe-area-context';
<SafeAreaProvider style={{ flex: 1, backgroundColor: '#000000' }}> {/* your app */}
iOS Setup
Step 1: Launch Screen Storyboard
-> Open ios/YourApp/LaunchScreen.storyboard
-> Set Safe Area constraints for all views:-
Add your splash image with constraints:
Leading = Safe Area Leading (+16) Trailing = Safe Area Trailing (-16) Top = Safe Area Top (+44) Bottom = Safe Area Bottom (-34)
Step 2: Update AppDelegate.swift
Add exactly one line:
// AppDelegate.swift import SplashOverlay // Add this import
@main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
SplashOverlay.shared.show() // Add this line
// ... rest of your existing code
return true} }
🚀 Usage
Hide Splash (Both Platforms)
import { NativeModules } from 'react-native';
// Hide splash when app is ready const hideSplash = async () => { try { await NativeModules.SplashOverlay.hide(); console.log('Splash hidden'); } catch (error) { console.log('Splash already hidden or error:', error); } };
// Example with useEffect import { useEffect } from 'react';
useEffect(() => { // Hide after app loads const timer = setTimeout(hideSplash, 1500); return () => clearTimeout(timer); }, []);
📱 Demo Flow
- App launches → Native splash shows instantly
- React Native loads → JS calls NativeModules.SplashOverlay.hide()
- 250ms fade → App content visible
Contributing
License
MIT
