Open In App

Create an Expense Tracker using React-Native

Last Updated : 02 Jul, 2025
Summarize
Comments
Improve
Suggest changes
Share
Like Article
Like
Report

The Expense Tracker application, using React-Native, is an application where users can manage the transactions made and then visualize the expenditure with the help of a pie chart. The users can add transactions while providing the category of expenditure. The application will run on any device and is the scope of a much larger application. The application is cross-platform, which means it can run on any platform.

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 and Technologies Used

Approach

  • The application will have a single screen.
  • The application will have a pie chart and an add button.
  • On pressing the add button, a form will open. The form contains the expense name, amount, and category from a select option.
  • Finally, on pressing add, the expense list and chart are updated.
  • The expenses are displayed in the form of a scrollable list.
  • Each expense tile will also have a delete button to remove it from the list.
  • We are using react-native-picker for a dropdown.
  • We used react-native-chart-kit.

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: Update Dependencies

The updated dependencies in the package.json file will look like this.

"dependencies": {
"expo": "53.0.15",
"expo-status-bar": "~2.2.3",
"react": "18.2.0",
"react-native": "0.79.0",
"react-native-chart-kit": "^6.12.0",
"@react-native-picker/picker": "2.11.1"
}

Step 4: Start Coding

Example: We will place the state variables in the App.js and load all the components declared in the respective files here. The code is divided into different files to make it readable.

App.js
// App.js

import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { Button, SafeAreaView, Text, View } from "react-native";
import { PieChart } from "react-native-chart-kit";
import styles from "./styles";
import Addform from "./add_expense";
import ExpenseComponent from "./expense_component";

export default function App() {
	// Define state variables using the useState hook
	const [name, setName] = useState("");
	const [amount, setAmount] = useState("");
	const [category, setCategory] = useState("Food");
	const [expenses, setExpenses] = useState([]);
	const categories = ["Food", "Clothes", "Bills", "Others"];
	const [addForm, setAddForm] = useState(false);

	// Function to open the add expense form
	const addExpense = () => {
		setAddForm(true);
	};

	// Initialize the chart data with default values
	const [chartData, setChartData] = useState([
		{
			name: "Food",
			amount: 0,
			color: "#e62d20",
			legendFontColor: "#7F7F7F",
			legendFontSize: 15,
		},
		{
			name: "Clothes",
			amount: 0,
			color: "#27e620",
			legendFontColor: "#7F7F7F",
			legendFontSize: 15,
		},
		{
			name: "Bills",
			amount: 0,
			color: "#1c6bd9",
			legendFontColor: "#7F7F7F",
			legendFontSize: 15,
		},
		{
			name: "Others",
			amount: 0,
			color: "#5adbac",
			legendFontColor: "#7F7F7F",
			legendFontSize: 15,
		},
	]);

	// Render the components and UI
	return (
		<SafeAreaView style={styles.container}>
			<StatusBar style="auto" />
			<Text style={styles.heading}>Welcome to GeeksforGeeks</Text>
			<Text style={styles.heading2}>
				Expense Tracker using React-Native
			</Text>

			{/* Render the PieChart component with data */}
			<PieChart
				data={chartData}
				width={300}
				height={200}
				chartConfig={{
					backgroundGradientFrom: "#1E2923",
					backgroundGradientTo: "#08130D",
					color: (opacity = 1) => `rgba(26, 255, 146, ${opacity})`,
				}}
				accessor="amount"
				backgroundColor="transparent"
				paddingLeft="15"
				absolute
			/>

			{/* Conditional rendering: If addForm is true, 
				render the Addform component */}
			{addForm == true ? (
				<Addform
					name={name}
					setName={setName}
					amount={amount}
					setAmount={setAmount}
					category={category}
					setCategory={setCategory}
					categories={categories}
					setExpenses={setExpenses}
					expenses={expenses}
					chartData={chartData}
					setChartData={setChartData}
					setAddForm={setAddForm}
				/>
			) : (
				/* If addForm is false, render the "Add Expense" button */
				<View style={styles.row}>
					<Button
						onPress={addExpense}
						color="green"
						style={styles.addButton}
						title="Add Expense"
					/>
				</View>
			)}

			{/* Render the ExpenseComponent */}
			<ExpenseComponent
				expenses={expenses}
				setExpenses={setExpenses}
				chartData={chartData}
				setChartData={setChartData}
			/>
		</SafeAreaView>
	);
}
add_expense.js
// add_expense.js

import { Picker } from "@react-native-picker/picker";
import { Button, Text, TextInput, View } from "react-native";
import styles from "./styles";

// Define the Addform component which is used to add new expenses
export default function Addform({
	name,
	setName,
	amount,
	setAmount,
	category,
	setCategory,
	categories,
	setExpenses,
	expenses,
	chartData,
	setChartData,
	setAddForm,
}) {
	return (
		<View>
			<Text style={styles.heading3}>Add Form</Text>

			{/* Input field for expense name */}
			<Text style={styles.label}>Expense Name</Text>
			<TextInput
				onChangeText={(value) => setName(value)}
				value={name}
				style={styles.textInput}
				placeholder="Enter the expense name"
			/>

			{/* Input field for expense amount */}
			<Text style={styles.label}>Amount</Text>
			<TextInput
				keyboardType="numeric"
				onChangeText={(value) => {
					// Ensure only numeric values are entered for the amount
					value = value.replace(/[^0-9]/g, "");
					setAmount(value);
				}}
				value={amount}
				style={styles.textInput}
				placeholder="Amount"
			/>

			{/* Dropdown to select expense category */}
			<Text style={styles.label}>Category</Text>
			<Picker
				style={styles.textInput}
				selectedValue={category}
				onValueChange={(itemValue, itemIndex) => setCategory(itemValue)}
			>
				{categories.map((category, index) => {
					return (
						<Picker.Item
							key={index}
							label={category}
							value={category}
						/>
					);
				})}
			</Picker>

			{/* Buttons to add or cancel expense */}
			<View style={styles.row}>
				{/* Add Expense button */}
				<Button
					onPress={() => {
						let amountNumber = parseInt(amount);
						if (amountNumber <= 0 || name == "") {
							// Validation: Ensure valid amount 
							// and name are entered
							alert("Please enter a valid amount and name");
							return;
						}

						// Add the new expense to the list of expenses
						setExpenses([
							...expenses,
							{
								id: new Date().getTime(),
								category,
								name,
								amount: amountNumber,
							},
						]);

						// Update the chart data to reflect the new expense
						let newChartData = [...chartData];
						let index = newChartData.findIndex(
							(item) => item.name == category
						);
						newChartData[index].amount += amountNumber;
						setChartData(newChartData);

						// Reset form fields and close the form
						setAddForm(false);
						setName("");
						setAmount("");
						setCategory("Food");
					}}
					title="Add Expense"
				/>

				{/* Cancel button to close the form
					without adding an expense */}
				<Button
					onPress={() => {
						setAddForm(false);
					}}
					title="Cancel"
				/>
			</View>
		</View>
	);
}
expense_component.js
// expense_component.js

import { Alert, Button, ScrollView, Text, View } from "react-native";
import styles from "./styles";

export default function ExpenseComponent({
	expenses,
	setExpenses,
	chartData,
	setChartData,
}) {
	return (
		<ScrollView
			style={{
				marginBottom: 80,
			}}
		>
			{expenses.map((expense) => {
				console.log(expense);
				return (
					<ExpenseListTile
						key={expense.id}
						expense={expense}
						chartData={chartData}
						expenses={expenses}
						setChartData={setChartData}
						setExpenses={setExpenses}
					/>
				);
			})}
		</ScrollView>
	);
}
const ExpenseListTile = ({
	expense,
	expenses,
	setExpenses,
	chartData,
	setChartData,
}) => {
	return (
		<View style={styles.expenseTile}>
			<Text style={styles.expenseTileText}>{expense.name}</Text>
			<Text style={styles.expenseTileText}>{expense.category}</Text>
			<Text style={styles.expenseTileText}>{expense.amount}</Text>
			<Button
				onPress={() => {
					Alert.alert("Delete", "Are you sure you want to delete?", [
						{
							text: "Yes",
							onPress: () => {
								let newExpenses = [...expenses];
								let index = newExpenses.findIndex(
									(item) => item.id == expense.id
								);
								newExpenses.splice(index, 1);
								setExpenses(newExpenses);
								let newChartData = [...chartData];
								let index2 = newChartData.findIndex(
									(item) => item.name == expense.category
								);
								newChartData[index2].amount -= expense.amount;
								setChartData(newChartData);
							},
						},
						{
							text: "No",
							onPress: () => {
								console.log("No");
							},
						},
					]);
				}}
				title="Delete"
			/>
		</View>
	);
};
styles.js
// styles.js

import { StyleSheet } from "react-native";
// styles sheet to store all the styles in one place
const styles = StyleSheet.create({
	row: {
		flexDirection: "row",
		justifyContent: "space-evenly",
	},
	container: {
		backgroundColor: "#fff",
		height: "100%",
		marginTop: 50,
	},
	heading: {
		color: "green",
		fontSize: 30,
		textAlign: "center",
		fontWeight: "bold",
	},
	addButton: {
		padding: 10,
		margin: 10,
	},
	heading2: {
		color: "black",
		fontSize: 25,
		textAlign: "center",
		fontWeight: "bold",
	},
	heading3: {
		color: "black",
		fontSize: 20,
		textAlign: "center",
	},
	label: {
		color: "black",
		fontSize: 16,
		textAlign: "left",
		fontWeight: "bold",
		marginLeft: 10,
	},
	expenseTile: {
		// column with 3 cells
		flexDirection: "row",
		justifyContent: "space-between",
		backgroundColor: "lightgrey",
		width: "95%",
		padding: 10,
		margin: 10,
	},
	expenseTileText: {
		fontSize: 20,
		width: "22%",
		textAlign: "center",
	},
	formAdd: {
		// display: "none",
	},
	textInput: {
		borderRadius: 12,
		borderColor: "black",
		borderWidth: 1,
		padding: 10,
		margin: 10,
	},
});
export default styles;

Output:

Explanation

  • The ExpenseComponent holds each of the expense elements and displays them.
  • chartData is used to display the chart, and expenses is used to hold the individual expenses.
  • We toggle the form using the addForm boolean variable.
  • On pressing the Add Button, it is toggled so that we can show/hide the form.

Next Article

Similar Reads