Skip to content

Creating a Personal Finance Tracker with React and Firebase

Updated: at 03:27 AM

In this tutorial, we’ll walk through the process of building a personal finance tracker using React for the frontend and Firebase for backend services. This project is perfect for beginners looking to gain hands-on experience with modern web development technologies.

Setting up the Development Environment

Before we dive into coding, let’s set up our development environment:

  1. Install Node.js and npm (Node Package Manager) from nodejs.org.
  2. Open your terminal and create a new React project:
npx create-react-app personal-finance-tracker
cd personal-finance-tracker
npm start

Your default browser should open with a new React app running.

Firebase Setup

Now, let’s set up Firebase:

  1. Go to firebase.google.com and create a new project.
  2. Install the Firebase SDK in your React project:
npm install firebase
  1. Create a src/firebase.js file and add your Firebase configuration:
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const firebaseConfig = {
  // Your Firebase configuration object
};

const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);

Designing the User Interface

Let’s create a simple UI for our finance tracker. Replace the contents of src/App.js with:

import React, { useState } from 'react';

function App() {
  const [description, setDescription] = useState('');
  const [amount, setAmount] = useState('');
  const [type, setType] = useState('expense');

  const handleSubmit = (e) => {
    e.preventDefault();
    // We'll implement this later
  };

  return (
    <div className="App">
      <h1>Personal Finance Tracker</h1>
      <form onSubmit={handleSubmit}>
        <input
          type="text"
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          placeholder="Description"
          required
        />
        <input
          type="number"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          placeholder="Amount"
          required
        />
        <select value={type} onChange={(e) => setType(e.target.value)}>
          <option value="expense">Expense</option>
          <option value="income">Income</option>
        </select>
        <button type="submit">Add Transaction</button>
      </form>
    </div>
  );
}

export default App;

Implementing Core Functionality

Now, let’s add the ability to track transactions and calculate the balance:

import React, { useState, useEffect } from 'react';
import { db } from './firebase';
import { collection, addDoc, onSnapshot } from 'firebase/firestore';

function App() {
  // ... previous state variables ...
  const [transactions, setTransactions] = useState([]);
  const [balance, setBalance] = useState(0);

  useEffect(() => {
    const unsubscribe = onSnapshot(collection(db, 'transactions'), (snapshot) => {
      const newTransactions = snapshot.docs.map(doc => ({
        id: doc.id,
        ...doc.data()
      }));
      setTransactions(newTransactions);
      calculateBalance(newTransactions);
    });

    return () => unsubscribe();
  }, []);

  const calculateBalance = (transactions) => {
    const newBalance = transactions.reduce((acc, transaction) => {
      return transaction.type === 'income' 
        ? acc + parseFloat(transaction.amount) 
        : acc - parseFloat(transaction.amount);
    }, 0);
    setBalance(newBalance);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    await addDoc(collection(db, 'transactions'), {
      description,
      amount: parseFloat(amount),
      type
    });
    setDescription('');
    setAmount('');
  };

  return (
    <div className="App">
      {/* ... previous JSX ... */}
      <h2>Balance: ${balance.toFixed(2)}</h2>
      <h3>Transactions:</h3>
      <ul>
        {transactions.map(transaction => (
          <li key={transaction.id}>
            {transaction.description} - ${transaction.amount} ({transaction.type})
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

Adding User Authentication

To secure our app, let’s add user authentication:

  1. Enable Authentication in your Firebase project and set up Email/Password sign-in.
  2. Install the necessary Firebase auth package:
npm install firebase/auth
  1. Update your firebase.js file:
import { getAuth } from 'firebase/auth';

// ... previous code ...

export const auth = getAuth(app);
  1. Create a new Login.js component:
import React, { useState } from 'react';
import { auth } from './firebase';
import { signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'firebase/auth';

function Login({ setUser }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [isSignUp, setIsSignUp] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      if (isSignUp) {
        await createUserWithEmailAndPassword(auth, email, password);
      } else {
        await signInWithEmailAndPassword(auth, email, password);
      }
    } catch (error) {
      console.error(error);
      alert(error.message);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
        required
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
        required
      />
      <button type="submit">{isSignUp ? 'Sign Up' : 'Login'}</button>
      <button type="button" onClick={() => setIsSignUp(!isSignUp)}>
        Switch to {isSignUp ? 'Login' : 'Sign Up'}
      </button>
    </form>
  );
}

export default Login;
  1. Update App.js to include authentication:
import React, { useState, useEffect } from 'react';
import { auth } from './firebase';
import { onAuthStateChanged } from 'firebase/auth';
import Login from './Login';

function App() {
  // ... previous state variables ...
  const [user, setUser] = useState(null);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
    });

    return () => unsubscribe();
  }, []);

  if (!user) {
    return <Login setUser={setUser} />;
  }

  // ... rest of the component ...
}

export default App;

Enhancing the Application

To make our app more useful, let’s add expense categorization:

  1. Update the form in App.js to include a category field:
<select value={category} onChange={(e) => setCategory(e.target.value)}>
  <option value="food">Food</option>
  <option value="transportation">Transportation</option>
  <option value="entertainment">Entertainment</option>
  <option value="utilities">Utilities</option>
  <option value="other">Other</option>
</select>
  1. Include the category when adding a new transaction.

  2. Add a simple pie chart to visualize expenses by category using a library like Chart.js:

npm install react-chartjs-2 chart.js

Create a new component called ExpenseChart.js:

import React from 'react';
import { Pie } from 'react-chartjs-2';
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';

ChartJS.register(ArcElement, Tooltip, Legend);

function ExpenseChart({ transactions }) {
  const expensesByCategory = transactions
    .filter(t => t.type === 'expense')
    .reduce((acc, t) => {
      acc[t.category] = (acc[t.category] || 0) + parseFloat(t.amount);
      return acc;
    }, {});

  const data = {
    labels: Object.keys(expensesByCategory),
    datasets: [
      {
        data: Object.values(expensesByCategory),
        backgroundColor: [
          '#FF6384',
          '#36A2EB',
          '#FFCE56',
          '#4BC0C0',
          '#9966FF'
        ]
      }
    ]
  };

  return <Pie data={data} />;
}

export default ExpenseChart;

Include this chart in your App.js.

Testing and Debugging

To ensure our app works correctly, let’s add some basic tests:

  1. Create a new file App.test.js:
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';

test('renders login form when user is not authenticated', () => {
  render(<App />);
  const emailInput = screen.getByPlaceholderText(/email/i);
  const passwordInput = screen.getByPlaceholderText(/password/i);
  expect(emailInput).toBeInTheDocument();
  expect(passwordInput).toBeInTheDocument();
});

// Add more tests as needed
  1. Run the tests:
npm test

Deployment

Finally, let’s deploy our app to Firebase Hosting:

  1. Build your React app:
npm run build
  1. Install the Firebase CLI:
npm install -g firebase-tools
  1. Initialize Firebase Hosting:
firebase init hosting
  1. Deploy your app:
firebase deploy

Conclusion

Congratulations! You’ve built a personal finance tracker using React and Firebase. This project has introduced you to key concepts in modern web development, including:

To further improve your app, consider adding features like:

Remember, the key to becoming a proficient developer is practice and continuous learning. Keep building and expanding on this project to deepen your skills!