Expense Tracker Project

*This is just a screenshot. The actual demo piece and Github repository linkes are located below
Overview
The React Expense Tracker with Firebase is a web application designed to help users manage and monitor their personal finances efficiently. Users can add, edit, and delete expense entries, categorize their spending, and view summaries of their financial activity over time. The application leverages React for a responsive and interactive user interface, while Firebase provides real-time data storage, authentication, and synchronization across devices.
This project aims to provide a simple yet powerful tool for individuals who want to keep track of their daily expenses, set budgets, and gain insights into their spending habits. By integrating Firebase, the app ensures that user data is securely stored in the cloud and accessible from anywhere.
Programming Languages / Libraries / Frameworks / Databases Used
Basically, this is a Web platform project which runs in any moren browser. JavaScript, HTML, CSS were used as main programming languages for this project and React, Vite, Recharts, @vitejs/plugin-react (React support for Vite), and @types/react, @types/react-dom (TypeScript type definitions for React) for libraries / frameworks. For database, Firebase Realtime Database was used according to package.json file.
How Does It Work? (Workflow)


Here’s how the files in your project communicate and work together, forming the workflow of the expense tracker app:
1. src/firebase.js
Purpose: Sets up and exports the Firebase Firestore database connection.
How it works:
Initializes Firebase using environment variables for configuration.
Exports the Firestore database instance as db.
Who uses it:
Imported by App.jsx to interact with the Firestore databas
2. src/App.jsx
Purpose: The main React component that manages the app’s state, handles user input, and interacts with Firestore.
How it works:
Imports db from firebase.js:
import { useState, useEffect } from "react";
import { db } from "./firebase";
import { collection, addDoc, deleteDoc, doc, onSnapshot } from "firebase/firestore";
import ExpensePieChart from './components/ExpensePieChart';
Allows App.jsx to read from and write to the Firestore database.
State Management:
Uses React’s useState to track expenses, form inputs, and selected category.
Real-time Data Sync:
Uses onSnapshot from Firestore to listen for real-time updates to the expenses collection. When data changes in Firestore, the local expenses state updates automatically.
Add/Delete Expense:
addDoc adds a new expense to Firestore.
deleteDoc removes an expense from Firestore.
Data Preparation for Chart:
Aggregates expenses by category to prepare data for the pie chart.
Renders UI:
Displays a form for adding expenses.
Lists all expenses with delete buttons.
Renders the ExpensePieChart component, passing the aggregated data as a prop.
function App() {
const [expenses, setExpenses] = useState([]);
const [desc, setDesc] = useState("");
const [amount, setAmount] = useState("");
const [category, setCategory] = useState("Food"); // default value
// Real-time updates listener
useEffect(() => {
const unsubscribe = onSnapshot(collection(db, "expenses"), (snapshot) => {
setExpenses(snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })));
});
return () => unsubscribe();
-
It sets up a real-time listener to a Firestore collection called "expenses" when the component mounts.
-
OnSnapshot(listener) to Firestone collection 'expenses' when the component mounts.
-
Whenever the collection changes (add, update, delete), it maps over the documents in the snapshot. -
Creates an array of expense objects, each with an id (the document ID) and the rest of the document data. -
Updates the local state (setExpenses) with this array.
}, []);
// Add new expense
const handleAddExpense = async (e) => {
e.preventDefault();
if (!desc || !amount) return;
await addDoc(collection(db, "expenses"), {
desc,
amount: parseFloat(amount),
category,
created: new Date()
});
setDesc("");
setAmount("");
setCategory("Food"); // reset to default if you want
};
// Delete expense
const handleDelete = async (id) => {
await deleteDoc(doc(db, "expenses", id));
};
-
Adds a new expense to the Firestore "expenses" collection. This Prevents the default form submission.
-
Checks if desc (description) and amount are provided; if not, it exits. Adds a new document to the "expenses" collection with:
-
desc (description)
-
amount (converted to a number)
-
category
-
created (current date/time)
-
Resets the input fields (desc, amount, and category).
// Prepare data for the pie chart
const chartData = Object.values(
expenses.reduce((acc, exp) => {
if (!acc[exp.category]) acc[exp.category] = { name: exp.category, value: 0 };
acc[exp.category].value += Number(exp.amount) || 0;
return acc;
}, {})
);
return (
<div style={{
maxWidth: 400,
margin: "2rem auto",
fontFamily: "sans-serif",
backgroundColor: "#b8e3ff",
padding: "2rem",
borderRadius: "10px",
boxShadow: "0 2px 4px rgba(0,0,0,0.1)"
}}>
<h1>Expense Tracker</h1>
<h2>
Total Spent: $
{expenses.reduce((sum, exp) => sum + (Number(exp.amount) || 0), 0)}
</h2>
<form onSubmit={handleAddExpense} style={{ marginBottom: "1rem" }}>
<input
placeholder="Description"
value={desc}
onChange={e => setDesc(e.target.value)}
style={{ marginRight: 8 }}
/>
<input
type="number"
placeholder="Amount"
value={amount}
onChange={e => setAmount(e.target.value)}
style={{ marginRight: 8 }}
/>
<select
value={category}
onChange={e => setCategory(e.target.value)}
style={{ marginRight: 8 }}
>
<option value="Food">Food</option>
<option value="Rent">Rent</option>
<option value="Transport">Transport</option>
{/* Add more categories as needed */}
</select>
<button type="submit">Add</button>
</form>
<ul>
{expenses.map(exp => (
<li key={exp.id}>
{exp.desc} ({exp.category}): ${exp.amount}
<button
onClick={() => handleDelete(exp.id)}
style={{ marginLeft: 8, color: "red" }}
>
Delete
</button>
</li>
))}
</ul>
<ExpensePieChart data={chartData} />
</div>
);
}
What function App() does: the function manages all the state variables like expenses, desc, amount, category) which controls the app's data. Also it uses useEffect and Firestore onSnapshot as listerns for database update. It keeps the UI synced with the database. Also, it is responsible for the button features, display and pie chart rendering.
3. src/components/ExpensePieChart.jsx
Purpose: Displays a pie chart of expenses by category.
How it works:
Receives data prop from App.jsx:
This prop contains the aggregated expense data by category.
Renders Pie Chart:
Uses the recharts library to render a pie chart, coloring each category differently.
Displays Tooltip and Legend:
For better visualization and understanding of the data.
The role of function ExpensePieChart{} is (non-design part):
The function receives the 'data' prop from its parent 'App.jsx'. Then it runs the map() then iterates over the data array and create a <Cell /> for each category. This is for letting each slice of pie chart corresponds to a category in the data.
Note: This component does nothing with processing or transforming the data: it simply renders what's given.
Also it does not manage or mutate any state.
What would be pros and cons (and possible future improvements)?
Pro1: Real-time updates keeps the data synced to the database and the changes immediately reflected in the UI.
Pro2: Seperation of files which gives seperation of concerns: App.jsx, ExpensePieChart.jsx, and firebase.js share each responsibilities.
Pro3: Using firebase removes the burden of own backend management or database server
Pro4: Visually it makes easy to understand the overall budget status.
Pro5: Scalable for additional features.
Ex) better design, adding more categories etc.
Con1: Anyone with access to the app can read/write expenses. No user accounts or privacy.
Con2: Minimal validation (e.g., only checks for empty fields). No feedback for failed database operations or invalid input.
Con3: All expenses are stored in a single Firestore collection, so all users see the same data. Not multi-user.
Con4: Only supports adding, deleting, and viewing expenses. No editing, filtering, or exporting data.
Con5: Fine for small projects, but with many users or lots of data, performance and cost could become issues.
Summary:
This project is a great starting point for learning React and Firebase, and for building a simple, real-time expense tracker. For production or multi-user use, it might be a better option to add authentication, better validation, user-specific data, and more robust error handling.