Open In App

Create a Coin Flipping App using React-Native

Last Updated : 25 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In this article we'll create a coin flipping app using React-Native. It allows users to simulate the flip of a coin and displays the result as either "Heads" or "Tails" with a corresponding image. The user can see the count of head and tails and can even reset the score.

Playground

Note: This Section is to interact with the app which you are going to build.

Prerequisites :

  • Node.js and npm are installed on your computer
  • Expo CLI (Expo is a framework for building React Native apps)
  • code editor (e.g., Visual Studio Code)

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-name

Project Structure:

Step 2: Run  Application

Start the server by using the following command.

npx expo start

Then, 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:

  • The app uses React Native to create a user interface.
  • It has a button that, when pressed, triggers a function to flip the coin.
  • The coin flip is simulated using a random number (0 or 1).
  • Depending on the random result, the app displays the "Heads" or "Tails" corresponding image.

Let's dive into the code in detail.

Import libraries:

Import required libraries at the top of the file.

JavaScript
// Import necessary hooks and components from React and React Native
import { useState, useRef } from "react";
import {
	View,                // Container component for layout
	Text,                // Component for displaying text
	StyleSheet,          // Utility for creating styles
	TouchableOpacity,    // Button-like component for touch handling
	Animated,            // For animations
	Easing,              // For animation easing functions
} from "react-native";
import { Audio } from "expo-av";


StyleSheet:

Create a StyleSheet to style components like container, title, coinContainer, etc.

JavaScript
// Styles for the components
const styles = StyleSheet.create({
  container: {
    flex: 1,                   // Take up full screen
    alignItems: "center",      // Center items horizontally
    justifyContent: "center",  // Center items vertically
  },
  title: {
    fontSize: 24,              // Large font size
    fontWeight: "bold",        // Bold text
    marginBottom: 20,          // Space below title
  },
  coinContainer: {
    marginBottom: 30,          // Space below coin image
  },
  coinImage: {
    width: 150,                // Coin image width
    height: 150,               // Coin image height
  },
  countContainer: {
    flexDirection: "row",      // Arrange counts in a row
    marginBottom: 10,          // Space below counts
  },
  count: {
    marginRight: 20,           // Space between counts
  },
  countText: {
    fontSize: 18,              // Font size for counts
    fontWeight: "bold",        // Bold text
    color: "#007BFF",          // Blue color
  },
  buttonRow: {
    flexDirection: "row",      // Arrange buttons in a row
  },
  button: {
    backgroundColor: "#007BFF",// Button background color
    padding: 10,               // Padding inside button
    margin: 10,                // Space between buttons
    borderRadius: 5,           // Rounded corners
  },
  buttonText: {
    color: "white",            // Button text color
    fontWeight: "bold",        // Bold button text
  },
});


Title Text:

This title explains what the app does. We use the text "Coin Flip App" to show that the app is to flip a Coin.

JavaScript
{/* Title text */}
<Text style={styles.title}>Coin Flip App</Text>


- Flip Coin Button:

This button is used to call the flipCoin function which have logic to flip the coin and It is designed by wrapping a TouchableOpacity component on a Text component having text "Flip Coin".

JavaScript
{/* Flip Coin Button */}
<TouchableOpacity 
    style={styles.button} 
    onPress={flipCoin}
>
    <Text style={styles.buttonText}>Flip Coin</Text>
</TouchableOpacity>


- flipCoin function:

This function is used to manage the coin flip process and its animation. It uses Math.floor and Math.random to determine whether the result is heads or tails. It also uses animations, audio to make the flip look and sound good. Finally, it updates some state variables: coinSide (whether it's Head or Tail), headsCount (how many heads have been flipped so far), and tailsCount (how many tails have been flipped so far) by calling the appropriate functions: setCoinSide, setHeadsCount, and setTailsCount.

JavaScript
// Function to handle coin flip logic and animation with audio
	const flipCoin = async () => {
		// Play coin flip audio
		try {
			const { sound } = await Audio.Sound.createAsync(
				{ uri: "https://media.geeksforgeeks.org/wp-content/uploads/20250524122236583887/coin-flip-audio.mp3" },
				{ shouldPlay: true }
			);
			// Unload sound after playback
			sound.setOnPlaybackStatusUpdate((status) => {
				if (status.didJustFinish) {
					sound.unloadAsync();
				}
			});
		} catch (e) {
			// Handle audio error silently
			console.error("Audio error:", e);
		}

		// Start the spin: animate rotateY from 0deg to 720deg over 1s with a custom cubic-bezier
		Animated.timing(flipAnimation, {
			toValue: 2, // 0 -> 720deg (2 * 360deg)
			duration: 1000,
			easing: Easing.bezier(0.68, -0.55, 0.27, 1.55),
			useNativeDriver: true,
		}).start();

		// Change image at halfway (500ms)
		setTimeout(() => {
			const randomSide = Math.floor(Math.random() * 2);
			if (randomSide === 0) {
				setCoinSide("Heads");
				setHeadsCount((prev) => prev + 1);
			} else {
				setCoinSide("Tails");
				setTailsCount((prev) => prev + 1);
			}
		}, 500);

		// Reset animation value after animation completes (1s)
		setTimeout(() => {
			flipAnimation.setValue(0);
		}, 1000);
	};


- Flip Coin UI:

It displays a coin flipping animation by using two image URLs in the code.

JavaScript
{/* Coin image container */}
<View style={styles.coinContainer}>
{/* Show the coin image if coinSide is set */}
{coinSide && (
  // Animated image for coin flip
  <Animated.Image
    source={{
      // Set image source based on coin side
      uri: coinSide === "Heads"
        ? "https://media.geeksforgeeks.org/wp-content/uploads/20250523110516074355/heads.png"
        : "https://media.geeksforgeeks.org/wp-content/uploads/20250523110416893474/tail.png",
    }}
    style={[
  styles.coinImage,
      {
        // Apply flip animation using rotateY
        transform: [
          {
            rotateY: flipAnimation.interpolate({
              inputRange: [0, 1],  // Input range for interpolation
              outputRange: ["0deg", "180deg"],    // Output range for rotation
            }),
          },
        ],
      },
    ]}
  />
)}
</View>


- Heads & Tails Count:

This part of code display the count of heads and Tails that have been flipped so far using Text Component and Holds the Text Component using View Component.

JavaScript
// State to keep track of the number of heads
const [headsCount, setHeadsCount] = useState(0);
// State to keep track of the number of tails
const [tailsCount, setTailsCount] = useState(0);

<View style={styles.countContainer}>
    {/* Head's Count */}
    <View style={styles.count}>
      <Text style={styles.countText}>Heads: {headsCount}</Text>
    </View>
    {/* Tail's Count */}
    <View style={styles.count}>
      <Text style={styles.countText}>Tails: {tailsCount}</Text>
    </View>
</View>


- Reset button & resetCounts function:

This section contains,

  • UI for Reset button using TouchableOpacity component wrapped on Text component having text "Reset", when user taps on the button then, it will call resetCount function.
  • resetCount function is used to redefine the heads and tails count to zero by calling setHeadsCount and setTailsCount with a parameter as zero.
JavaScript
// State to keep track of the number of heads
const [headsCount, setHeadsCount] = useState(0);
// State to keep track of the number of tails
const [tailsCount, setTailsCount] = useState(0);

// Function to reset both heads and tails counts
const resetCounts = () => {
    setHeadsCount(0);    // Reset heads count to 0
    setTailsCount(0);    // Reset tails count to 0
};

{/* Reset Button Code */}
<TouchableOpacity style={styles.button} onPress={resetCounts}>
    <Text style={styles.buttonText}>Reset</Text>
</TouchableOpacity>


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:

App.js
// Import necessary hooks and components from React and React Native
import { useState, useRef } from "react";
import {
	View,                // Container component for layout
	Text,                // Component for displaying text
	StyleSheet,          // Utility for creating styles
	TouchableOpacity,    // Button-like component for touch handling
	Animated,            // For animations
	Easing,              // For animation easing functions
} from "react-native";
import { Audio } from "expo-av";

// Main App component
const App = () => {
	// State to keep track of the current coin side ("Heads" or "Tails")
	const [coinSide, setCoinSide] = useState("Heads");
	// State to keep track of the number of heads
	const [headsCount, setHeadsCount] = useState(0);
	// State to keep track of the number of tails
	const [tailsCount, setTailsCount] = useState(0);

	// Animated value for the coin flip animation
	const flipAnimation = useRef(new Animated.Value(0)).current;
	// Import Audio from expo-av at the top of your file:
	// import { Audio } from "expo-av";

	// Function to handle coin flip logic and animation with audio
	const flipCoin = async () => {
		// Play coin flip audio
		try {
			const { sound } = await Audio.Sound.createAsync(
				{ uri: "https://media.geeksforgeeks.org/wp-content/uploads/20250524122236583887/coin-flip-audio.mp3" },
				{ shouldPlay: true }
			);
			// Unload sound after playback
			sound.setOnPlaybackStatusUpdate((status) => {
				if (status.didJustFinish) {
					sound.unloadAsync();
				}
			});
		} catch (e) {
			// Handle audio error silently
			console.error("Audio error:", e);
		}

		// Start the spin: animate rotateY from 0deg to 720deg over 1s with a custom cubic-bezier
		Animated.timing(flipAnimation, {
			toValue: 2, // 0 -> 720deg (2 * 360deg)
			duration: 1000,
			easing: Easing.bezier(0.68, -0.55, 0.27, 1.55),
			useNativeDriver: true,
		}).start();

		// Change image at halfway (500ms)
		setTimeout(() => {
			const randomSide = Math.floor(Math.random() * 2);
			if (randomSide === 0) {
				setCoinSide("Heads");
				setHeadsCount((prev) => prev + 1);
			} else {
				setCoinSide("Tails");
				setTailsCount((prev) => prev + 1);
			}
		}, 500);

		// Reset animation value after animation completes (1s)
		setTimeout(() => {
			flipAnimation.setValue(0);
		}, 1000);
	};
	// Function to reset both heads and tails counts
	const resetCounts = () => {
		setHeadsCount(0);    // Reset heads count to 0
		setTailsCount(0);    // Reset tails count to 0
	};

	// Render the UI
	return (
		<View style={styles.container}>
			{/* App title */}
			<Text style={styles.title}>Coin Flip App</Text>
			{/* Coin image container */}
			<View style={styles.coinContainer}>
				{/* Show the coin image if coinSide is set */}
				{coinSide && (
					// Animated image for coin flip
					<Animated.Image
						source={{
							// Set image source based on coin side
							uri: coinSide === "Heads"
								? "https://media.geeksforgeeks.org/wp-content/uploads/20250523110516074355/heads.png"
								: "https://media.geeksforgeeks.org/wp-content/uploads/20250523110416893474/tail.png",
						}}
						style={[
							styles.coinImage,
							{
								// Apply flip animation using rotateY
								transform: [
									{
										rotateY: flipAnimation.interpolate({
											inputRange: [0, 1],  // Input range for interpolation
											outputRange: ["0deg", "180deg"],    // Output range for rotation
										}),
									},
								],
							},
						]}
					/>
				)}
			</View>
			<View style={styles.countContainer}>
				<View style={styles.count}>
					<Text style={styles.countText}>Heads: {headsCount}</Text>
				</View>
				<View style={styles.count}>
					<Text style={styles.countText}>Tails: {tailsCount}</Text>
				</View>
			</View>
			{/* Row of buttons for flipping and resetting */}
			<View style={styles.buttonRow}>
				<TouchableOpacity style={styles.button} onPress={flipCoin}>
					<Text style={styles.buttonText}>Flip Coin</Text>
				</TouchableOpacity>
				<TouchableOpacity style={styles.button} onPress={resetCounts}>
					<Text style={styles.buttonText}>Reset</Text>
				</TouchableOpacity>
			</View>
		</View>
	);
};

// Styles for the components
const styles = StyleSheet.create({
	container: {
		flex: 1,                   // Take up full screen
		alignItems: "center",      // Center items horizontally
		justifyContent: "center",  // Center items vertically
	},
	title: {
		fontSize: 24,              // Large font size
		fontWeight: "bold",        // Bold text
		marginBottom: 20,          // Space below title
	},
	coinContainer: {
		marginBottom: 30,          // Space below coin image
	},
	coinImage: {
		width: 150,                // Coin image width
		height: 150,               // Coin image height
	},
	countContainer: {
		flexDirection: "row",      // Arrange counts in a row
		marginBottom: 10,          // Space below counts
	},
	count: {
		marginRight: 20,           // Space between counts
	},
	countText: {
		fontSize: 18,              // Font size for counts
		fontWeight: "bold",        // Bold text
		color: "#007BFF",          // Blue color
	},
	buttonRow: {
		flexDirection: "row",      // Arrange buttons in a row
	},
	button: {
		backgroundColor: "#007BFF",// Button background color
		padding: 10,               // Padding inside button
		margin: 10,                // Space between buttons
		borderRadius: 5,           // Rounded corners
	},
	buttonText: {
		color: "white",            // Button text color
		fontWeight: "bold",        // Bold button text
	},
});

// Export the App component as default
export default App;

Output:



Next Article

Similar Reads