Menu

Building the ExpenseTracker Component

Building the ExpenseTracker Component

The ExpenseTracker component contains the application's complete expense-tracking functionality. It allows users to enter their monthly income, add expenses by selecting or creating a category, view all added expenses, remove expenses when needed, and automatically calculate the Total Expense and Remaining Balance.

Whenever a user adds or deletes an expense, the component's state is updated, causing the UI to re-render and instantly display the latest values.

To keep the overall project organized, this functionality was developed inside a separate components folder as a child component, while the App component acts as the parent component that imports and renders it.

Additionally, toLocaleString("en-IN") is used to display income, expenses, and balance values in the standard Indian number format, making large amounts easier to read and understand.

File Path: ExpenseTracker\src\components\expenseTracker.jsx

import React, { useState } from "react";


function ExpenseTracker() {
    const [income, setIncome] = useState("");




    const [category, setCategory] = useState("Apparel");
    const [amount, setAmount] = useState("");


    const [expenses, setExpenses] = useState([]);


    const [customCategory, setCustomCategory] = useState("");




    const addExpense = () => {




        if (!income || Number(income) <= 0) {
            alert("Please enter your monthly income first!");
            return;
        }




        if (!amount || Number(amount) <= 0) return;


        const expenseName =
            category === "Other Expense"
                ? customCategory.trim()
                : category;


        if (!expenseName) {
            alert("Please name the expense");
            return;
        }


        const categoryExists = expenses.some(
            (expense) =>
                expense.category.toLowerCase() ===
                expenseName.toLowerCase()
        );


        if (categoryExists) {
            alert("This category already exists!");
            return;
        }


        const newExpense = {
            id: Date.now(),
            category: expenseName,
            amount: Number(amount),
        };


        setExpenses([...expenses, newExpense]);
        setAmount("");
        setCustomCategory("");
    };




    const deleteExpense = (id) => {
        setExpenses(expenses.filter((expense) => expense.id !== id));
    };


    const totalExpense = expenses.reduce(
        (total, expense) => total + expense.amount,
        0
    );


    const balance = Number(income || 0) - totalExpense;






    return (


        <>




            <div className="head-text flex flex-col items-center  w-full max-w-6xl rounded-2xl py-6 px-4">


                <h1 className="mb-2 text-3xl md:text-4xl text-center">
                    Expense Tracker
                </h1>


                <h2 className="text-base md:text-lg text-center font-semibold">
                    <i>Manage your daily expenses</i>
                </h2>


            </div>


            <div className="data-display-section flex flex-col lg:flex-row w-full max-w-6xl gap-6  rounded-2xl p-4 justify-between items-center mt-6">


                <div className="total-income  p-6 rounded-2xl w-full lg:w-[35%]">




                    <h2 className="mb-4 text-xl md:text-2xl text-center font-semibold">
                        Monthly Income
                    </h2>


                    <input
                        type="text"
                        value={income}
                        onChange={(e) => setIncome(e.target.value)}
                        placeholder="₹ Enter monthly income"
                        className="w-full border border-slate-600 rounded-xl px-4 py-3 outline-none bg-white text-black"
                    />




                </div>


                <div className="summary-section  rounded-2xl p-6 w-full lg:w-[60%]">


                    <h2 className="mb-4 text-xl md:text-2xl text-center font-semibold">
                        Summary
                    </h2>


                    {/* <div className="boxes flex flex-col md:flex-row gap-4"> */}
                    <div className="boxes grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">


                        <div className="income-box flex-1 bg-white p-5 rounded-2xl shadow-sm border border-slate-200">
                            <h3 className="text-base md:text-lg text-slate-700">Income</h3>
                            <p className="text-xl md:text-2xl font-bold">
                                ₹{Number(income).toLocaleString("en-IN")}
                            </p>
                        </div>


                        <div className="expense-box flex-1 bg-white p-5 rounded-2xl shadow-sm border border-slate-200">
                            <h3 className="text-base md:text-lg text-slate-700">Expense</h3>
                            <p className="text-xl md:text-2xl font-bold">
                                ₹{totalExpense.toLocaleString("en-IN")}
                            </p>
                        </div>


                        <div className="total-balance-box flex-1 bg-white p-5 rounded-2xl shadow-sm border border-slate-200">
                            <h3 className="text-base md:text-lg text-slate-700">Balance</h3>
                            <p className="text-xl md:text-2xl font-bold">
                                ₹{balance.toLocaleString("en-IN")}
                            </p>
                        </div>


                    </div>


                </div>


            </div>


            <div className="expense-section flex flex-col lg:flex-row mt-6 mb-6 gap-6 w-full max-w-6xl rounded-2xl p-4">


                <div className="add-expense flex flex-col p-6 rounded-2xl w-full lg:w-1/2">


                    <h2 className="text-xl md:text-2xl text-center font-semibold">
                        Expenses
                    </h2>


                    <label htmlFor="expenses" className="text-lg mt-4">
                        Category
                    </label>


                    <select
                        name="expenses"
                        id="expenses"
                        value={category}
                        onChange={(e) => setCategory(e.target.value)}
                        className="border border-slate-600 rounded-xl px-4 py-3 mt-2 outline-none"
                    >
                        <option>Other Expense</option>
                        <option>Apparel</option>
                        <option>Food</option>
                        <option>Travel</option>
                        <option>Mobile Recharge</option>
                    </select>




                    {category === "Other Expense" && (
                        <input
                            type="text"
                            value={customCategory}
                            onChange={(e) => setCustomCategory(e.target.value)}
                            placeholder="Name the expense"
                            className="border border-slate-600 rounded-xl px-4 py-3 mt-4 outline-none"
                        />
                    )}


                    <label htmlFor="expense-amount" className="text-lg mt-4">
                        Amount (in ₹):
                    </label>


                    <input
                        type="number"
                        id="expense-amount"
                        name="expense-amount"
                        min="1"
                        value={amount}
                        onChange={(e) => setAmount(e.target.value)}
                        placeholder="Enter amount"
                        className="border border-slate-600 rounded-xl px-4 py-3 mt-2 outline-none"
                    />


                    <button
                        onClick={addExpense}
                        className="w-full p-3 rounded-lg text-white bg-black mt-4 hover:opacity-90"
                    >
                        Add Expense
                    </button>


                </div>


             
                <div className="expense-list bg-white p-6 rounded-2xl shadow-sm border border-gray-400 w-full lg:w-1/2">


                    <h2 className="mb-4 text-xl md:text-2xl text-center font-semibold">
                        Expense List
                    </h2>


                    {expenses.length === 0 ? (
                        <p className="text-lg text-slate-900 my-6">
                            No expenses added yet.
                        </p>
                    ) : (
                        expenses.map((expense) => (
                            <div
                                key={expense.id}
                                className="text-lg flex justify-between py-3 border-b border-slate-200"
                            >
                                <span>{expense.category}</span>


                                <div className="flex items-center gap-4">


                                    <span>
                                        ₹{expense.amount.toLocaleString("en-IN")}
                                    </span>


                                    <button
                                        onClick={() => deleteExpense(expense.id)}
                                        className="text-red-500 hover:text-red-400 font-extrabold"
                                        title="Clear Expense"
                                    >
                                        ✕
                                    </button>


                                </div>


                            </div>
                        ))
                    )}


                    <div className="text-lg flex justify-between py-3 font-bold">
                        <span>Total Expense:</span>
                        <span>₹{totalExpense.toLocaleString("en-IN")}</span>
                    </div>


                </div>


            </div>




        </>
    );
}


export default ExpenseTracker;