React Js Expense Tracker with Tailwind CSS | Expense Tracker React | React Budget Tracker App

react-expense-tracker
react-expense-tracker

Hi, welcome here in this tutorial we are going to develop a React-Hook-Expense-Tracker App and also use Tailwind CSS for styling, The prerequisites for this project are as follows:

  • Node.js should be installed in your system.
  • Create a brand new ReactJs project. Just copy the code below:
				
					code to create React js project:

npx create-react-app my-app

after it run the below command:

cd my-app

after it run the below commands to install Tailwind css into your project:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

after it run the below command to run your react app:

npm start

				
			

The brand-new React Js project should look like this:

brand new react js app
brand new react js app

Here is your code for the App.js file, copy the code below and paste it into your App.js file:

				
					import logo from './logo.svg';
import './App.css';
import Budget from './components/Budget';
import Remaining from './components/Remaining';
import ExpenseList from './components/ExpenseList';
import AddExpenseForm from './components/AddExpenseForm';
import Spent from './components/Spent';
import { AppProvider } from './context/AppContext';

function App() {
  return (
<>
<AppProvider>
<div className="App">
      <div className='flex justify-center'>
        <Budget />
        <Remaining />
        <Spent />
      </div>

      <ExpenseList />
      <AddExpenseForm />
    </div>
</AppProvider>
</>
  );
}

export default App;
				
			

Here is your code for the index.css file, copy the code below and paste it into your index.css file:

				
					@tailwind base;
@tailwind components;
@tailwind utilities;

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

code {
  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
    monospace;
}

				
			

Here is your code for tailwind.config.js copy the code below and paste it into tailwind.config.js.

				
					/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
				
			

Make a new component folder and in the folder make the following new files and name then according to the instructions below.

Make a new file ‘AddExpenseForm.jsx’ and then Copy the code below and paste it into AddExpenseForm.jsx

				
					import React, { useState, useContext } from 'react'
import { v4 as uuidv4 } from 'uuid';
import { AppContext } from '../context/AppContext';

const AddExpenseForm = () => {

    const [name, setName] = useState('')
    const [cost, setCost] = useState('')

    const { dispatch } = useContext(AppContext)

    const onSubmit = (e) => {
        e.preventDefault();

        const expense = {
            id: uuidv4(),
            name: name,
            cost: parseInt(cost)
        };

        dispatch({
            type: 'ADD_EXPENSE',
            payload: expense,
        })

        setName('');
        setCost('')

    }

    const btnDis = name== '' || cost == '' ? 'disabled' : '';

  return (
    <>
            <div className="w-full grid justify-items-center mt-1">
            <form onSubmit={onSubmit} className="w-1/2 bg-slate-50 rounded px-8 pt-6 pb-8 mb-4 flex flex-row">
                <div className="mb-4 text-left m-5">
                    <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor='name'>
                        Name
                    </label>
                    <input className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" value={name} onChange={(e) => setName(e.target.value)} id="name" type="text" placeholder="where you spend" />
                </div>
                <div className="mb-6 text-left m-5">
                    <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="cost">
                        Cost (pkr)
                    </label>
                    <input className="appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" value={cost} onChange={(e) => setCost(e.target.value)} id="cost" type="number" placeholder="300" />
                </div>
                <div className="flex items-center justify-between m-5">
                    <button className='bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-12 mt-3 rounded focus:outline-none focus:shadow-outline' type="submit" disabled={btnDis}>
                        Add
                    </button>
                </div>
            </form>
        </div>
    </>
  )
}

export default AddExpenseForm
				
			

Make a new file ‘Budget.jsx’ and then Copy the code below and paste it into Budget.jsx

				
					import React, { useContext } from 'react'
import { AppContext } from '../context/AppContext'

const Budget = () => {
    const { budget } = useContext(AppContext);
    return (
        <div className="w-80 bg-gray-100 border border-gray-400 text-gray-700 px-4 py-3 m-2 rounded relative" role="alert">
            <strong className="font-bold">Budget: </strong>
            <span className="block sm:inline">{budget} pkr</span>
        </div>
    )
}

export default Budget
				
			

Make a new file ‘ExpenseItem.jsx’ and then Copy the code below and paste it into ExpenseItem.jsx

				
					import React, { useContext } from 'react'
import { TiDelete } from 'react-icons/ti';
import { AppContext } from '../context/AppContext';


const ExpenseItem = (props) => {

    const { dispatch } = useContext(AppContext)

    const handleDeleteExpense = () => {
        dispatch({
            type: 'DELETE_EXPENSE',
            payload: props.id
        });
    };

    return (
        <li className='w-1/2 flex justify-between p-1 m-1 bg-slate-50 rounded'>
            <span className='pr-40'>{props.name}</span>
            <div className='flex justify-center'>
                <span className='bg-blue-600 m-1 pr-2 pl-2 rounded text-white'>
                    {props.cost} pkr
                </span>
                <TiDelete size='1.5em' className='m-1' onClick={handleDeleteExpense}></TiDelete>
            </div>
        </li>
    )
}

export default ExpenseItem
				
			

Make a new file ‘ExpenseList.jsx’ and then Copy the code below and paste it into ExpenseList.jsx

				
					import React, { useContext } from 'react'
import { AppContext } from '../context/AppContext';
import ExpenseItem from './ExpenseItem';

const ExpenseList = () => {
    const {expenses} = useContext (AppContext);

    return (
            <ul className='w-full grid justify-items-center'>
                {expenses.map((expense) => (
                    <ExpenseItem id={expense.id} name={expense.name} cost={expense.cost} />
                ))}
            </ul>
    )
}

export default ExpenseList
				
			

Make a new file ‘Remaining.jsx’ and then Copy the code below and paste it into Remaining.jsx

				
					import React, { useContext } from 'react';
import { AppContext } from '../context/AppContext';

const Remaining = () => {

    const { expenses, budget } = useContext(AppContext);

	const totalExpenses = expenses.reduce((total, item) => {
		return (total = total + item.cost);
	}, 0);

    const alertType = totalExpenses > budget ? 'bg-red-100 border-red-400 text-red-700' : 'bg-green-100 border-green-400 text-green-700';


    return (
        <div className={`w-80 border  px-4 py-3 m-2 rounded relative ${alertType}`} role="alert">
            <strong className="font-bold">Remaining: </strong>
            <span className="block sm:inline">{budget - totalExpenses} pkr</span>
        </div>
    )
}

export default Remaining
				
			

Make a new file ‘Spent.jsx’ and then Copy the code below and paste it into Spent.jsx

				
					import React, { useContext } from 'react'
import { AppContext } from '../context/AppContext'

const Spent = () => {

    const { expenses } = useContext(AppContext)
    const totalExpenses = expenses.reduce((total, item) => {
        return (total += item.cost);
    }, 0);
    return (
        <div className="w-80 bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 m-2 rounded relative" role="alert">
            <strong className="font-bold">Spent: </strong>
            <span className="block sm:inline">{totalExpenses} pkr</span>
        </div>
    )
}

export default Spent
				
			

Make a context folder and then make a new file ‘AppContext.jsx’ and then Copy the code below and paste it into AppContext.jsx

				
					import { useReducer, createContext } from 'react';

const AppReducer = (state, action) => {
    switch (action.type) {
        case 'ADD_EXPENSE':
            return {
                ...state,
                expenses: [...state.expenses, action.payload],
            };
        case 'DELETE_EXPENSE':
            return {
                ...state,
                expenses: state.expenses.filter(
                    (expense) => expense.id !== action.payload
                ),
            };
        default:
            return state;
    }
};

const initialState = {
    budget: prompt("Please enter your budget: "),
    // budget: 12000,
    expenses: [
        // { id: 12, name: 'shopping', cost: 40 },
        // { id: 13, name: 'holiday', cost: 400 },
        // { id: 14, name: 'car service', cost: 50 },
    ],
};

export const AppContext = createContext();

export const AppProvider = (props) => {
    const [state, dispatch] = useReducer(AppReducer, initialState);

    return (
        <AppContext.Provider
            value={{
                budget: state.budget,
                expenses: state.expenses,
                dispatch,
            }}
        >
            {props.children}
        </AppContext.Provider>
    );
};
				
			

After writing the code into the files (don’t forget to save it ,) you can run the command below:

				
					npm start