Managing finances can feel like navigating a maze. Keeping track of income, expenses, and savings often involves spreadsheets, notebooks, and a lot of manual data entry. Wouldn’t it be great to have a simple, intuitive tool that helps you visualize your financial health at a glance? In this tutorial, we’ll build a web-based budget tracker using Next.js, a powerful React framework, to create a user-friendly and interactive application.
Why Build a Budget Tracker?
A budget tracker isn’t just about numbers; it’s about understanding your spending habits, identifying areas where you can save, and ultimately, taking control of your financial future. By visualizing your income and expenses, you gain clarity and can make informed decisions. This project is ideal for beginners and intermediate developers because it combines fundamental concepts like state management, data fetching, and user interface (UI) design in a manageable scope. It’s also a practical application that you can use daily!
What We’ll Build
Our budget tracker will have the following features:
- Income Entry: Users can add their income sources and amounts.
- Expense Entry: Users can log expenses with categories and amounts.
- Data Visualization: A simple chart (we’ll use a library) to show income vs. expenses.
- Basic Reporting: Display of total income, expenses, and remaining balance.
- User-Friendly Interface: A clean and intuitive UI for easy interaction.
We’ll use Next.js for its server-side rendering (SSR) and static site generation (SSG) capabilities, which can improve performance and SEO. We’ll also use a UI library like Material UI or Tailwind CSS to speed up development and create a polished look. Let’s get started!
Prerequisites
Before you begin, ensure you have the following:
- Node.js and npm (or yarn): Make sure you have Node.js and npm (Node Package Manager) or yarn installed on your system.
- A Code Editor: A code editor like VS Code or Sublime Text.
- Basic HTML, CSS, and JavaScript Knowledge: Familiarity with these languages will be helpful.
Step-by-Step Guide
1. Setting Up the Next.js Project
First, let’s create a new Next.js project using the `create-next-app` command. Open your terminal and run:
npx create-next-app budget-tracker
Navigate into your project directory:
cd budget-tracker
2. Installing Dependencies
Next, install the necessary dependencies. We’ll use a charting library (e.g., Chart.js) for data visualization and a UI library (e.g., Material UI or Tailwind CSS) for styling. For this example, let’s use Chart.js and Material UI. Run the following commands:
npm install chart.js @mui/material @emotion/react @emotion/styled
Or with Yarn:
yarn add chart.js @mui/material @emotion/react @emotion/styled
3. Project Structure and File Setup
Your project structure should look similar to this:
budget-tracker/
├── node_modules/
├── pages/
│ └── index.js
├── public/
├── styles/
│ └── globals.css
├── .gitignore
├── next.config.js
├── package-lock.json
├── package.json
└── README.md
We’ll be working primarily in the `pages/index.js` file for our main application logic and UI. You might also create a `components` directory to store reusable components, which we’ll do later.
4. Creating the UI
Let’s start building the UI. Open `pages/index.js` and replace its content with the following code. This sets up the basic layout with Material UI components:
import React, { useState } from 'react';
import { Container, Typography, TextField, Button, Grid, Paper } from '@mui/material';
import Chart from 'chart.js/auto'; // Import Chart.js
import { Bar } from 'react-chartjs-2'; // Import Bar component
export default function Home() {
const [income, setIncome] = useState('');
const [expenseDescription, setExpenseDescription] = useState('');
const [expenseAmount, setExpenseAmount] = useState('');
const [expenses, setExpenses] = useState([]);
const [incomeTotal, setIncomeTotal] = useState(0);
const [expenseTotal, setExpenseTotal] = useState(0);
// Function to add income (Placeholder)
const handleAddIncome = () => {
const parsedIncome = parseFloat(income);
if (!isNaN(parsedIncome)) {
setIncomeTotal(incomeTotal + parsedIncome);
setIncome('');
}
};
// Function to add expense
const handleAddExpense = () => {
const parsedAmount = parseFloat(expenseAmount);
if (!isNaN(parsedAmount) && expenseDescription) {
setExpenses([...expenses, { description: expenseDescription, amount: parsedAmount }]);
setExpenseTotal(expenseTotal + parsedAmount);
setExpenseDescription('');
setExpenseAmount('');
}
};
const remainingBalance = incomeTotal - expenseTotal;
const chartData = {
labels: ['Income', 'Expenses'],
datasets: [
{
label: 'Income vs. Expenses',
data: [incomeTotal, expenseTotal],
backgroundColor: ['rgba(75,192,192,0.6)', 'rgba(255,99,132,0.6)'],
borderColor: ['rgba(75,192,192,1)', 'rgba(255,99,132,1)'],
borderWidth: 1,
},
],
};
const chartOptions = {
scales: {
y: {
beginAtZero: true,
},
},
};
return (
Budget Tracker
Add Income
setIncome(e.target.value)}
style={{ marginBottom: '10px', marginRight: '10px' }}
/>
<Button>Add Income</Button>
Add Expense
setExpenseDescription(e.target.value)}
style={{ marginBottom: '10px', marginRight: '10px' }}
/>
setExpenseAmount(e.target.value)}
style={{ marginBottom: '10px', marginRight: '10px' }}
/>
<Button>Add Expense</Button>
Summary
Total Income: ${incomeTotal.toFixed(2)}
Total Expenses: ${expenseTotal.toFixed(2)}
Remaining Balance: ${remainingBalance.toFixed(2)}
Budget Overview
Expenses List
<ul>
{expenses.map((expense, index) => (
<li>{expense.description}: ${expense.amount.toFixed(2)}</li>
))}
</ul>
);
}
This code sets up the basic structure of the budget tracker. It includes:
- Import of necessary components from Material UI and Chart.js.
- State variables to manage income, expense description, expense amount, the list of expenses, and totals.
- Input fields for income and expenses.
- Buttons to add income and expenses.
- Display of total income, expenses, and remaining balance.
- A bar chart visualizing income vs. expenses using Chart.js.
- A list to display the expenses added.
5. Implementing State Management
We use the `useState` hook from React to manage the state of our application. This includes the income, expense description, expense amount, the list of expenses, total income, and total expenses. Here’s a breakdown of how state is managed:
- `income` and `setIncome`: Manages the input value for income.
- `expenseDescription` and `setExpenseDescription`: Manages the input value for expense description.
- `expenseAmount` and `setExpenseAmount`: Manages the input value for expense amount.
- `expenses` and `setExpenses`: An array to store all expense objects (description and amount).
- `incomeTotal` and `setIncomeTotal`: Holds the total sum of all income entries.
- `expenseTotal` and `setExpenseTotal`: Holds the total sum of all expense entries.
The `handleAddIncome` and `handleAddExpense` functions update the state when the user adds income or an expense, respectively. They parse the input values, validate them, and update the appropriate state variables. The remaining balance is calculated dynamically.
6. Adding Income and Expenses
The `handleAddIncome` function is designed to handle adding income. It parses the input value, ensures it’s a valid number, and updates the `incomeTotal` state. The `handleAddExpense` function does the same for expenses: it takes the description and the amount, validates them, and updates the `expenses` array and `expenseTotal` state.
Here’s how these functions work:
const handleAddIncome = () => {
const parsedIncome = parseFloat(income);
if (!isNaN(parsedIncome)) {
setIncomeTotal(incomeTotal + parsedIncome);
setIncome('');
}
};
const handleAddExpense = () => {
const parsedAmount = parseFloat(expenseAmount);
if (!isNaN(parsedAmount) && expenseDescription) {
setExpenses([...expenses, { description: expenseDescription, amount: parsedAmount }]);
setExpenseTotal(expenseTotal + parsedAmount);
setExpenseDescription('');
setExpenseAmount('');
}
};
Notice the use of `parseFloat` to convert the input strings to numbers. Also, the use of the spread operator (`…expenses`) to add a new expense to the `expenses` array without modifying the original array (immutability). This is good practice in React.
7. Data Visualization with Chart.js
We’ll use Chart.js to create a bar chart to visualize the income and expenses. We’ve already imported the necessary components and set up the `chartData` and `chartOptions` variables.
Here’s the relevant code for the chart:
const chartData = {
labels: ['Income', 'Expenses'],
datasets: [
{
label: 'Income vs. Expenses',
data: [incomeTotal, expenseTotal],
backgroundColor: ['rgba(75,192,192,0.6)', 'rgba(255,99,132,0.6)'],
borderColor: ['rgba(75,192,192,1)', 'rgba(255,99,132,1)'],
borderWidth: 1,
},
],
};
const chartOptions = {
scales: {
y: {
beginAtZero: true,
},
},
};
The `chartData` object defines the data for the chart, including labels, data points, and styling. The `chartOptions` object configures the chart’s appearance, such as the y-axis starting at zero. The `Bar` component from `react-chartjs-2` renders the chart, passing in these `data` and `options` props.
8. Displaying Summary and Expense List
The summary section displays the total income, expenses, and the remaining balance. The expense list dynamically renders the expenses added by the user, using the `map` function to iterate over the `expenses` array.
Summary
Total Income: ${incomeTotal.toFixed(2)}
Total Expenses: ${expenseTotal.toFixed(2)}
Remaining Balance: ${remainingBalance.toFixed(2)}
Expenses List
<ul>
{expenses.map((expense, index) => (
<li>{expense.description}: ${expense.amount.toFixed(2)}</li>
))}
</ul>
The `.toFixed(2)` method is used to format the numbers to two decimal places for currency representation.
9. Styling with Material UI
Material UI provides a set of pre-built React components, which we’ve used for styling the app. This includes `Container`, `Typography`, `TextField`, `Button`, `Grid`, and `Paper`. Material UI simplifies the process of creating a visually appealing and responsive UI.
For example, the following code creates a styled input field:
setIncome(e.target.value)}
style={{ marginBottom: '10px', marginRight: '10px' }}
/>
The `variant=”outlined”` prop gives the input field an outlined appearance, and the `style` prop allows us to add custom styles, such as margins.
10. Running the Application
To run the application, execute the following command in your terminal:
npm run dev
Or with Yarn:
yarn dev
This will start the development server, and you can view your budget tracker in your browser (usually at `http://localhost:3000`).
Common Mistakes and Troubleshooting
Here are some common mistakes and how to fix them:
- Incorrect Dependency Installation: Make sure you install all dependencies correctly using `npm install` or `yarn add`. Double-check the package names for typos.
- Missing Imports: Ensure that you import all the necessary components from Material UI and Chart.js. Check for import errors in your console.
- State Not Updating: If the UI doesn’t update when you add income or expenses, double-check your state updates. Make sure you’re using the correct `set` functions (e.g., `setIncome`, `setExpenses`). Also, ensure you are not directly mutating state.
- Chart Not Rendering: Verify that you’ve imported the `Bar` component correctly and that your `chartData` object is structured properly. Check the browser console for any Chart.js-related errors.
- CSS Issues: If the styling is not displaying correctly, check your CSS imports and make sure your CSS files are correctly linked.
Key Takeaways
- State Management: Understanding how to manage state with `useState` is fundamental to building interactive React applications.
- UI Libraries: Using UI libraries like Material UI or Tailwind CSS can significantly speed up development and provide a consistent look and feel.
- Data Visualization: Chart.js is a powerful library for visualizing data within your application.
- Component Structure: Breaking down your application into reusable components makes your code more maintainable and organized.
- Error Handling: Always validate user input (e.g., checking if the amount is a valid number) to prevent unexpected behavior.
FAQ
Here are some frequently asked questions:
- Can I store the data in a database? Yes, you can. For a production application, you would typically use a backend (e.g., Node.js with Express, or a serverless function) to store the data in a database (e.g., MongoDB, PostgreSQL). You would then fetch and save data using API calls.
- How can I add more features? You can add features such as expense categories, recurring expenses, budgeting goals, and more advanced reporting.
- How do I deploy this application? You can deploy your Next.js application to platforms like Vercel, Netlify, or AWS. These platforms handle the deployment process and provide a URL for your application.
- Can I use a different UI library? Absolutely! You can use any UI library you prefer, such as Tailwind CSS, Ant Design, or Chakra UI. The core concepts remain the same.
This tutorial provides a solid foundation for building a simple budget tracker. You can extend this project with more features and customize it to your specific needs. By practicing and experimenting, you’ll gain valuable experience in web development and financial management. As you continue to refine your application, consider adding features like user authentication, data persistence, and more advanced reporting capabilities to create a truly comprehensive financial tool. The journey of building your own budget tracker is a rewarding one, empowering you not only to code but also to better understand your finances.
