How to make splash animation in react-native with react-native-bootsplash and react-native-animated.
Hello Guys today I will show you to make a beautiful animated splash screen using react-native. So lets get started.
Start with react native setup
I assume you have already setup the react-native project. Please visit this links for necessary setups of react-native app, react-native navigation, react-native-reanimated.
Once this setups are done lets start with react-native bootsplash
Lest first install react-native-bootsplash
yarn add react-native-bootsplash
Next step is asset generation.
Create an "assets" folder in root and save the logo.png in it.
yarn react-native generate-bootsplash assets/logo.svg \ --platforms=android,ios \ --background=F5FCFF \ --logo-width=100 \ --assets-output=assets \ --flavor=main \ --html=index.html
To setup the splash screen in
ios
Edit theios/YourProjectName/
AppDelegate.mm
file:#import "AppDelegate.h" #import "RNBootSplash.h" // ⬅️ add the header import // … @implementation AppDelegate // … // ⬇️ Add this before file @end (when bridgeless is enabled) - (void)customizeRootView:(RCTRootView *)rootView { [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // ⬅️ initialize the splash screen } @end
To setup the splash screen in
android
- Edit your
android/app/src/main/res/values/styles.xml
file:
- Edit your
<resources>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<!-- Your base theme customization -->
</style>
<!-- BootTheme should inherit from Theme.BootSplash or Theme.BootSplash.EdgeToEdge -->
<style name="BootTheme" parent="Theme.BootSplash">
<item name="bootSplashBackground">@color/bootsplash_background</item>
<item name="bootSplashLogo">@drawable/bootsplash_logo</item>
<item name="postBootSplashTheme">@style/AppTheme</item>
</style>
</resources>
- Edit your
android/app/src/main/AndroidManifest.xml
file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- … -->
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"> <!-- Apply @style/AppTheme on .MainApplication -->
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:theme="@style/BootTheme"> <!-- Apply @style/BootTheme on .MainActivity -->
<!-- … -->
</activity>
</application>
</manifest>
- Finally edit your
android/app/src/main/java/com/yourprojectname/MainActivity.kt
file:
// Kotlin (react-native >= 0.73)
// …
// add these required imports:
import android.os.Bundle
import com.zoontek.rnbootsplash.RNBootSplash
class MainActivity : ReactActivity() { {
// …
override fun onCreate(savedInstanceState: Bundle?) {
RNBootSplash.init(this, R.style.BootTheme) // ⬅️ initialize the splash screen
super.onCreate(savedInstanceState) // super.onCreate(null) with react-native-screens
}
}
Once the setup is done
Create a two functional components 'SplashScreen' and 'HomeScreen'
💡Also make sure to add SplashScreen on top of Stack.Navigator
import {Button, StyleSheet, Text, View} from 'react-native';
import React from 'react';
function SplashScreen({navigation}: any) {
return (
<View style={styles.container}>
<Text>Splash screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
}
export default SplashScreen;
const styles = StyleSheet.create({
container: {flex: 1, justifyContent: 'center', alignItems: 'center'},
});
import {Button, StyleSheet, Text, View} from 'react-native';
import React from 'react';
function HomeScreen({navigation}: any) {
return (
<View style={styles.container}>
<Text>Home screen</Text>
</View>
);
}
export default HomeScreen;
const styles = StyleSheet.create({
container: {flex: 1, justifyContent: 'center', alignItems: 'center'},
});
In Splashscreen add this useEffect to hide the native splashscreen once lt is loaded.
React.useEffect(() => { const init = async () => { // …do multiple sync or async tasks }; init().finally(async () => { await BootSplash.hide({fade: true}); console.log('BootSplash has been hidden successfully'); }); }, []);
In splashScreen create this containers
const {height} = Dimensions.get('window'); const slideY = useSharedValue(height); const circleSize = useSharedValue(50); const circleStyle = useAnimatedStyle(() => { return { width: circleSize.value, height: circleSize.value, borderRadius: circleSize.value / 2, transform: [{translateY: slideY.value}], }; }); return ( <View style={styles.container}> <Image source={require('../../assets/argo_logo.png')} style={styles.logo} /> <Animated.View style={[styles.circle, circleStyle]} /> </View> )} export default SplashScreen; const styles = StyleSheet.create({ container: { backgroundColor: '#64B54E', flex: 1, justifyContent: 'center', alignItems: 'center', }, logo: { height: 130, width: '100%', objectFit: 'contain', marginTop: height > 875 ? -height * 0.05 : height * 0.02, //adjust this as per ur requirement }, circle: { backgroundColor: 'white', position: 'absolute', }, });
Lets start the animation
React.useEffect(() => { const animate = async () => { await new Promise(resolve => setTimeout(resolve, 500)); // Initial delay // Slide animation slideY.value = withTiming(height / 2 - 80, {duration: 1500}); await new Promise(resolve => setTimeout(resolve, 1500)); // Delay before circle animation // Circle animation circleSize.value = withTiming(2000, {duration: 1000}); await new Promise(resolve => setTimeout(resolve, 750)); // Delay before changing status bar // Change status bar StatusBar.setBackgroundColor('white'); StatusBar.setBarStyle('dark-content'); }; animate(); }, []); React.useEffect(() => { setTimeout(() => { navigation.replace('HomeScreen'); }, 3000); // Change the time as per your requirement }, []);
Meanwhile in HomeScreen component do this changes.
import {
Button,
Dimensions,
StyleSheet,
Text,
View,
Animated,
} from 'react-native';
import React from 'react';
const {height} = Dimensions.get('window');
function HomeScreen({navigation}: any) {
const slideAnim = React.useRef(new Animated.Value(height)).current;
React.useEffect(() => {
Animated.timing(slideAnim, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
}, []);
return (
<Animated.View
style={[styles.container, {transform: [{translateY: slideAnim}]}]}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</Animated.View>
);
}
export default HomeScreen;
const styles = StyleSheet.create({
container: {flex: 1, justifyContent: 'center', alignItems: 'center'},
});
Let me share you the final SplashScreen Code
import React from 'react';
import {Dimensions, Image, StatusBar, StyleSheet, View} from 'react-native';
import BootSplash from 'react-native-bootsplash';
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
const {height} = Dimensions.get('window');
const SplashScreen = ({navigation}: any) => {
React.useEffect(() => {
const init = async () => {
// …do multiple sync or async tasks
};
init().finally(async () => {
await BootSplash.hide({fade: true});
console.log('BootSplash has been hidden successfully');
});
}, []);
React.useEffect(() => {
setTimeout(() => {
navigation.replace('PhoneScreen');
}, 3000); // Change the time as per your requirement
}, []);
const slideY = useSharedValue(height);
const circleSize = useSharedValue(50);
const circleStyle = useAnimatedStyle(() => {
return {
width: circleSize.value,
height: circleSize.value,
borderRadius: circleSize.value / 2,
transform: [{translateY: slideY.value}],
};
});
React.useEffect(() => {
const animate = async () => {
await new Promise(resolve => setTimeout(resolve, 500)); // Initial delay
// Slide animation
slideY.value = withTiming(height / 2 - 80, {duration: 1500});
await new Promise(resolve => setTimeout(resolve, 1500)); // Delay before circle animation
// Circle animation
circleSize.value = withTiming(2000, {duration: 1000});
await new Promise(resolve => setTimeout(resolve, 750)); // Delay before changing status bar
// Change status bar
StatusBar.setBackgroundColor('white');
StatusBar.setBarStyle('dark-content');
};
animate();
}, []);
return (
<View style={styles.container}>
<Image
source={require('../../assets/argo_logo.png')}
style={styles.logo}
/>
<Animated.View style={[styles.circle, circleStyle]} />
</View>
);
};
export default SplashScreen;
const styles = StyleSheet.create({
container: {
backgroundColor: '#64B54E',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
logo: {
height: 130,
width: '100%',
objectFit: 'contain',
marginTop: height > 875 ? -height * 0.05 : height * 0.02,
},
circle: {
backgroundColor: 'white',
position: 'absolute',
},
});
Hope You liked it.... do provide feedback
Subscribe to my newsletter
Read articles from Harish Gautam directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by