React-Native is an open-source framework used to develop cross-platform applications i.e., you can write code in React-Native and publish it as an Android or IOS app. In this article, we will build a basic Spin the Bottle game in React-Native. This is a multi-player game. Usually, people sit in a round with a bottle at the center. When the bottle stops spinning, the turn goes to the person at whom the bottle's tip stops.
Playground
Note: This Section is to interact with the app which you are going to build.
Prerequisites
Step-by-Step Implementation
Step 1: Create a React Native Project
Now, create a project with the following command.
npx create-expo-app app-name --template
Note: Replace the app-name with your app name for example : react-native-demo-app
Next, you might be asked to choose a template. Select one based on your preference as shown in the image below. I am selecting the blank template because it will generate a minimal app that is as clean as an empty canvas in JavaScript.

It completes the project creation and displays a message: "Your Project is ready!" as shown in the image below.

Now go into your project folder, i.e., react-native-demo
cd app-nameProject Structure:

Step 2: Run Application
Start the server by using the following command.
npx expo startThen, the application will display a QR code.
- For the Android users,
- For the Android Emulator, press " a" as mentioned in the image below.
- For the Physical Device, download the " Expo Go " app from the Play Store. Open the app, and you will see a button labeled " Scan QR Code. " Click that button and scan the QR code; it will automatically build the Android app on your device.
- For iOS users, simply scan the QR code using the Camera app .
- If you're using a web browser, it will provide a local host link that you can use as mentioned in the image below.

Step 3: Start Coding
- Approach to create Spin the Bottle Game:
The game screen will display a bottle and a SPIN button. The bottle will start spinning when the SPIN button is clicked. The bottle will stop at a random position to indicate the turn of a person. The random position can be set using the Math.random() function. The spinning animation can be set using the Animated module of React-Native. The Spin the Bottle game can be used to play the famous games like Truth or Dare, Pictionary, Two Truths and a Lie etc.
Let's dive into the code in detail.
- Import libraries:
Import required libraries at the top of the file.
// Import required hooks and components from React and React Native
import { useRef } from 'react';
import {
View,
Pressable,
Text,
Animated,
Easing,
StyleSheet,
} from 'react-native';
- StyleSheet:
Create a StyleSheet to style components like container, geekText, bottle, etc.
// Styles for the components
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
geekText: {
marginBottom: '10%',
fontSize: 25,
color: '#228B22',
fontWeight: 'bold',
},
bottle: {
justifyContent: 'center',
alignItems: 'center',
},
rectangle3: {
width: 30,
height: 14,
backgroundColor: '#43E71A', // Top part of the bottle
},
rectangle2: {
width: 100,
height: 106,
backgroundColor: '#B4F6A3', // Neck of the bottle
borderTopLeftRadius: 40,
borderTopRightRadius: 40,
overflow: 'hidden',
},
rectangle1: {
width: 100,
height: 157,
backgroundColor: '#A2F74D', // Body of the bottle
},
spinButton: {
alignItems: 'center',
marginTop: '20%',
borderRadius: 5,
paddingTop: 6,
paddingBottom: 6,
paddingLeft: 15,
paddingRight: 15,
},
spinButtonText: {
fontSize: 18,
color: 'white',
userSelect: 'none', // Prevents accidental text selection on desktop
},
});
- Title Text:
This title explains what the app does. We use the text "Spin the bottle" to show that the app is to play the Spin the bottle Game.
{/* Title Text */}
<Text style={styles.geekText}>Spin the bottle</Text>
- Spin Button:
This button is used to call startSpinAnimation and the function contains the game logic to spin the bottle.
// Animated value that controls the rotation
const spinValue = useRef(new Animated.Value(0)).current;
// Ref to prevent multiple spins at the same time
const isSpinning = useRef(false);
// Function to start the spin animation
const startSpinAnimation = () => {
if (!isSpinning.current) {
isSpinning.current = true;
spinValue.setValue(0); // Reset the spin value before starting
Animated.timing(spinValue, {
// Randomize spin by adding a decimal to 10 full rotations
toValue: 10 + generateRandomDecimal(),
duration: 2000, // Duration of animation in milliseconds
easing: Easing.bezier(0.16, 1, 0.3, 1), // Smooth acceleration/deceleration
useNativeDriver: false, // Not using native driver as rotation is in degrees
}).start(() => {
isSpinning.current = false; // Mark as not spinning when animation completes
// Optional: Add logic here after spinning ends
});
}
};
{/* Spin Button */}
<Pressable
style={({ pressed }) => [
styles.spinButton,
{
backgroundColor: pressed ? '#0F9D58' : '#14EF37', // Button press feedback
},
]}
onPress={startSpinAnimation}>
<Text style={styles.spinButtonText}>SPIN</Text>
</Pressable>
- Display Bottle:
Below code is used to display the bottle which is spinning.
// Interpolating the animated value to degrees for rotation
const spin = spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'], // One full rotation
});
{/* Animated bottle built using nested Views */}
<Animated.View style={[styles.bottle, { transform: [{ rotate: spin }] }]}>
<View style={styles.rectangle3}></View>
<View style={styles.rectangle2}></View>
<View style={styles.rectangle1}></View>
</Animated.View>
Now, wrap all design code with a View component, return it from the App component, and place all methods and useStates within the App component. Ensure to export the App.
Complete Source Code
App.js:
// Import required hooks and components from React and React Native
import { useRef } from 'react';
import {
View,
Pressable,
Text,
Animated,
Easing,
StyleSheet,
} from 'react-native';
// Function to generate a random decimal between 0.00 and 1.00
const generateRandomDecimal = () => {
const randomNumber = Math.random();
return parseFloat(randomNumber.toFixed(2));
};
// Main component that implements the spinning bottle animation
const SpinBottle = () => {
// Animated value that controls the rotation
const spinValue = useRef(new Animated.Value(0)).current;
// Ref to prevent multiple spins at the same time
const isSpinning = useRef(false);
// Function to start the spin animation
const startSpinAnimation = () => {
if (!isSpinning.current) {
isSpinning.current = true;
spinValue.setValue(0); // Reset the spin value before starting
Animated.timing(spinValue, {
// Randomize spin by adding a decimal to 10 full rotations
toValue: 10 + generateRandomDecimal(),
duration: 2000, // Duration of animation in milliseconds
easing: Easing.bezier(0.16, 1, 0.3, 1), // Smooth acceleration/deceleration
useNativeDriver: false, // Not using native driver as rotation is in degrees
}).start(() => {
isSpinning.current = false; // Mark as not spinning when animation completes
// Optional: Add logic here after spinning ends
});
}
};
// Interpolating the animated value to degrees for rotation
const spin = spinValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'], // One full rotation
});
return (
<View style={styles.container}>
{/* Title Text */}
<Text style={styles.geekText}>Spin the bottle</Text>
{/* Animated bottle built using nested Views */}
<Animated.View style={[styles.bottle, { transform: [{ rotate: spin }] }]}>
<View style={styles.rectangle3}></View>
<View style={styles.rectangle2}></View>
<View style={styles.rectangle1}></View>
</Animated.View>
{/* Spin Button */}
<Pressable
style={({ pressed }) => [
styles.spinButton,
{
backgroundColor: pressed ? '#0F9D58' : '#14EF37', // Button press feedback
},
]}
onPress={startSpinAnimation}>
<Text style={styles.spinButtonText}>SPIN</Text>
</Pressable>
</View>
);
};
// App root component rendering the SpinBottle
export default function App() {
return <SpinBottle />;
}
// Styles for the components
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
geekText: {
marginBottom: '10%',
fontSize: 25,
color: '#228B22',
fontWeight: 'bold',
},
bottle: {
justifyContent: 'center',
alignItems: 'center',
},
rectangle3: {
width: 30,
height: 14,
backgroundColor: '#43E71A', // Top part of the bottle
},
rectangle2: {
width: 100,
height: 106,
backgroundColor: '#B4F6A3', // Neck of the bottle
borderTopLeftRadius: 40,
borderTopRightRadius: 40,
overflow: 'hidden',
},
rectangle1: {
width: 100,
height: 157,
backgroundColor: '#A2F74D', // Body of the bottle
},
spinButton: {
alignItems: 'center',
marginTop: '20%',
borderRadius: 5,
paddingTop: 6,
paddingBottom: 6,
paddingLeft: 15,
paddingRight: 15,
},
spinButtonText: {
fontSize: 18,
color: 'white',
userSelect: 'none', // Prevents accidental text selection on desktop
},
});