Open In App

Create a Bill Splitter App using React-Native

Last Updated : 30 Jun, 2025
Summarize
Comments
Improve
Suggest changes
Share
Like Article
Like
Report

We are going to implement the Bill Splitter App using React Native. Bill Splitter App is a mobile application that helps us divide expenses and bills among a group of people. For example, when we are dining at a restaurant with friends, going on a trip, or sharing household expenses with roommates.

To give you a better idea of what we’re going to create, let’s watch a demo video.

Demo Video

Playground

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


Prerequisites:

Approach:

  • Bill Splitter App is a mobile application that helps us split expenses and bills among a group of people.
  • It takes the name and money spent by each individual.
  • Then this App calculates the money equally among all.
  • We also included a calculate button in the app, so when the button is clicked, it shows the Name from whom to take money or whom to give money.
  • We also added a reset button in the app to reset the data.

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

Example:

  • Emulator will open after this command, and after opening of emulator, you are ready to work in VS Code to write the code.
  • Open the file and simply paste the source code
App.js
import React, { useState } from 'react';
import {
	View, Text, TextInput,
	TouchableOpacity, StyleSheet,
	ScrollView
} from 'react-native';

const BillSplitter = () => {
	const [participants, setParticipants] =
		useState(
			[
				{
					name: '',
					amount: ''
				}
			]);
	const [settleTransactions, setSettleTransactions] = useState([]);

	const addParticipant = () => {
		setParticipants(
			[...participants,
			{
				name: '',
				amount: ''
			}]);
	};

	const removeParticipant = (index) => {
		const updatedParticipants = [...participants];
		updatedParticipants.splice(index, 1);
		setParticipants(updatedParticipants);
	};

	const calculateSplit = () => {
		const totalExpense = participants.reduce(
			(total, participant) =>
				total + parseFloat(participant.amount || 0),
			0
		);
		const splitAmount =
			totalExpense / participants.length;

		const calculatedResults =
			participants.map((participant) => {
				const amount =
					(splitAmount - parseFloat(participant.amount)).toFixed(2);
				return {
					name: participant.name,
					amount: parseFloat(amount),
				};
			});

		const settleTransactions = [];
		let positiveBalances =
			calculatedResults.filter(
				(result) =>
					result.amount > 0);
		let negativeBalances =
			calculatedResults.filter(
				(result) =>
					result.amount < 0);

		while (positiveBalances.length > 0 &&
			negativeBalances.length > 0) {
			const payer = positiveBalances[0];
			const payee = negativeBalances[0];

			const settledAmount =
				Math.min(
					Math.abs(payer.amount),
					Math.abs(payee.amount));

			settleTransactions.push({
				payer: payer.name,
				payee: payee.name,
				amount: settledAmount.toFixed(2),
			});

			payer.amount -= settledAmount;
			payee.amount += settledAmount;

			if (Math.abs(payer.amount) < 0.005) {
				positiveBalances.shift();
			}

			if (Math.abs(payee.amount) < 0.005) {
				negativeBalances.shift();
			}
		}

		setSettleTransactions(settleTransactions);
	};

	const resetApp = () => {
		setParticipants([{ name: '', amount: '' }]);
		setSettleTransactions([]);
	};

	return (
		<View style={styles.container}>
			<Text style={styles.title}>Bill Splitter</Text>
			<ScrollView style={styles.participantList}>
				{participants.map((participant, index) => (
					<View key={index} style={styles.participantItem}>
						<TextInput
							style={styles.input}
							placeholder="Name"
							value={participant.name}
							onChangeText={(text) => {
								const updatedParticipants = [...participants];
								updatedParticipants[index].name = text;
								setParticipants(updatedParticipants);
							}}
						/>
						<TextInput
							style={styles.input}
							placeholder="Amount"
							value={participant.amount}
							onChangeText={(text) => {
								const updatedParticipants = [...participants];
								updatedParticipants[index].amount = text;
								setParticipants(updatedParticipants);
							}}
							keyboardType="numeric"
						/>
						<TouchableOpacity
							style={styles.removeButton}
							onPress={() => removeParticipant(index)}
						>
							<Text style={styles.removeButtonText}>Remove</Text>
						</TouchableOpacity>
					</View>
				))}
			</ScrollView>
			<TouchableOpacity style={styles.addButton} onPress={addParticipant}>
				<Text style={styles.addButtonText}>Add Participant</Text>
			</TouchableOpacity>
			<TouchableOpacity style={styles.calculateButton} 
				onPress={calculateSplit}>
				<Text style={styles.calculateButtonText}>Calculate</Text>
			</TouchableOpacity>
			<TouchableOpacity style={styles.resetButton} onPress={resetApp}>
				<Text style={styles.resetButtonText}>Reset</Text>
			</TouchableOpacity>

			{/* Settled Transactions Box */}
			<View style={styles.resultBox}>
				<Text style={styles.resultsTitle}>Settle Transactions</Text>
				<ScrollView style={styles.resultsList}>
					{settleTransactions.map((transaction, index) => (
						<Text key={index} style={styles.resultItem}>
							{transaction.payer}
							should pay
							{transaction.payee} ${transaction.amount}
						</Text>
					))}
				</ScrollView>
			</View>
		</View>
	);
};

const styles = StyleSheet.create({
	container: {
		flex: 1,
		padding: 20,
		backgroundColor: 'white',
	},
	title: {
		fontSize: 24,
		textAlign: 'center',
		marginVertical: 20,
	},
	participantList: {
		marginBottom: 20,
	},
	participantItem: {
		flexDirection: 'row',
		justifyContent: 'space-between',
		marginVertical: 10,
	},
	input: {
		flex: 1,
		height: 40,
		borderColor: 'gray',
		borderWidth: 1,
		borderRadius: 5,
		paddingHorizontal: 10,
	},
	addButton: {
		backgroundColor: '#007AFF',
		padding: 15,
		borderRadius: 5,
		alignItems: 'center',
		marginBottom: 10,
	},
	addButtonText: {
		color: 'white',
		fontSize: 16,
	},
	calculateButton: {
		backgroundColor: '#34C759',
		padding: 15,
		borderRadius: 5,
		alignItems: 'center',
	},
	calculateButtonText: {
		color: 'white',
		fontSize: 16,
	},
	resultsTitle: {
		fontSize: 20,
		marginVertical: 10,
	},
	resultsList: {
		marginBottom: 20,
	},
	resultItem: {
		fontSize: 16,
	},
	resetButton: {
		backgroundColor: '#FF3B30',
		padding: 15,
		borderRadius: 5,
		alignItems: 'center',
		marginTop: 10,
	},
	resetButtonText: {
		color: 'white',
		fontSize: 16,
	},
	removeButton: {
		backgroundColor: '#FF3B30',
		padding: 10,
		borderRadius: 100,
		alignItems: 'center',
		marginTop: 5,
		marginLeft: 5,
	},
	removeButtonText: {
		color: 'white',
		fontSize: 12,
	},
	resultBox: {
		borderWidth: 1,
		borderColor: '#ddd',
		borderRadius: 5,
		marginTop: 20,
		padding: 5,
		backgroundColor: '#CBF6B6',
		elevation: 5,
		shadowOffset: { width: 0, height: 2 },
		shadowOpacity: 0.3,
		shadowRadius: 3,
	},
});

export default BillSplitter;

Output:


Similar Reads