โ† Back to all guides
๐Ÿ’ธ

Build a Budget Bestie App

A cute expense tracker that actually makes you want to manage your money. No boring spreadsheets allowed.

Projectsbeginner7 min read

Build a Budget Bestie App

Budgeting apps are either way too complicated or way too ugly. Let's build one that's simple, cute, and actually helpful โ€” like a bestie who's good with money.

๐Ÿ’ก Pro tip

Want to see the finished app first? Try the live demo and come back to learn how it works!

What We're Building

  • Add expenses with categories (food, shopping, coffee, going out, subscriptions)
  • See a visual breakdown of where your money goes
  • Set monthly budgets per category
  • Get a vibe check on your spending (are you on track or nah?)

๐Ÿ’ฌ Denise says

I built my first budget app because I needed to understand where my money kept disappearing. Turns out it was Uber Eats. The app I built to track it was also how I learned about arrays, reduce(), and localStorage. Win-win (except for Uber Eats).

Step 1: The Data

1// Categories with colors and emojis
2const categories = [
3{ id: 'food', name: 'Food & Groceries', emoji: '๐Ÿ•', color: '#F4A7BB' },
4{ id: 'coffee', name: 'Coffee & Drinks', emoji: 'โ˜•', color: '#F0D9A0' },
5{ id: 'shopping', name: 'Shopping', emoji: '๐Ÿ›๏ธ', color: '#C4B8E8' },
6{ id: 'going-out', name: 'Going Out', emoji: '๐ŸŽ‰', color: '#A8D8EA' },
7{ id: 'subscriptions', name: 'Subscriptions', emoji: '๐Ÿ“ฑ', color: '#F2A5C0' },
8{ id: 'transport', name: 'Transport', emoji: '๐Ÿš—', color: '#F5C2D0' },
9{ id: 'self-care', name: 'Self Care', emoji: '๐Ÿ’…', color: '#E8E1F5' },
10{ id: 'other', name: 'Other', emoji: 'โœจ', color: '#FEF1C7' },
11];
12
13// What an expense looks like:
14const expense = {
15id: 1,
16amount: 6.50,
17category: 'coffee',
18note: 'Oat milk latte (I deserved it)',
19date: '2025-04-30',
20};

Step 2: Add Expense Form

1import { useState } from 'react';
2
3function AddExpense({ onAdd }) {
4const [amount, setAmount] = useState('');
5const [category, setCategory] = useState('food');
6const [note, setNote] = useState('');
7
8const handleSubmit = (e) => {
9e.preventDefault();
10if (!amount) return;
11
12onAdd({
13id: Date.now(),
14amount: parseFloat(amount),
15category,
16note,
17date: new Date().toISOString().split('T')[0],
18});
19
20// Reset form
21setAmount('');
22setNote('');
23};
24
25return (
26<form onSubmit={handleSubmit} className='add-expense'>
27<h3>Add Expense</h3>
28
29{/* Amount input */}
30<div className='amount-input'>
31<span className='dollar-sign'>$</span>
32<input
33type='number'
34step='0.01'
35placeholder='0.00'
36value={amount}
37onChange={e => setAmount(e.target.value)}
38/>
39</div>
40
41{/* Category picker โ€” emoji buttons */}
42<div className='category-grid'>
43{categories.map(cat => (
44<button
45key={cat.id}
46type='button'
47onClick={() => setCategory(cat.id)}
48className={`cat-btn ${category === cat.id ? 'selected' : ''}`}
49style={{ borderColor: category === cat.id ? cat.color : 'transparent' }}
50>
51<span>{cat.emoji}</span>
52<span className='cat-label'>{cat.name}</span>
53</button>
54))}
55</div>
56
57{/* Note */}
58<input
59type='text'
60placeholder='What was it for? (optional)'
61value={note}
62onChange={e => setNote(e.target.value)}
63className='note-input'
64/>
65
66<button type='submit' className='submit-btn'>
67Add Expense
68</button>
69</form>
70);
71}

Step 3: Spending Summary

1function SpendingSummary({ expenses, month }) {
2// Total spent this month
3const total = expenses.reduce((sum, e) => sum + e.amount, 0);
4
5// Spending by category
6const byCategory = categories.map(cat => {
7const catExpenses = expenses.filter(e => e.category === cat.id);
8const catTotal = catExpenses.reduce((sum, e) => sum + e.amount, 0);
9const percentage = total > 0 ? (catTotal / total) * 100 : 0;
10
11return {
12...cat,
13total: catTotal,
14percentage: Math.round(percentage),
15count: catExpenses.length,
16};
17}).filter(cat => cat.total > 0); // Only show categories with spending
18
19return (
20<div className='summary'>
21<h3>This Month</h3>
22<p className='total-amount'>${total.toFixed(2)}</p>
23
24{/* Visual bar breakdown */}
25<div className='spending-bar'>
26{byCategory.map(cat => (
27<div
28key={cat.id}
29className='bar-segment'
30style={{
31width: cat.percentage + '%',
32backgroundColor: cat.color,
33}}
34title={cat.name + ': $' + cat.total.toFixed(2)}
35/>
36))}
37</div>
38
39{/* Category breakdown list */}
40<div className='category-list'>
41{byCategory
42.sort((a, b) => b.total - a.total)
43.map(cat => (
44<div key={cat.id} className='category-row'>
45<span className='cat-dot' style={{ background: cat.color }} />
46<span className='cat-name'>{cat.emoji} {cat.name}</span>
47<span className='cat-amount'>${cat.total.toFixed(2)}</span>
48<span className='cat-percent'>{cat.percentage}%</span>
49</div>
50))
51}
52</div>
53</div>
54);
55}

Step 4: Vibe Check (Budget Status)

1function VibeCheck({ totalSpent, monthlyBudget }) {
2const percentage = (totalSpent / monthlyBudget) * 100;
3const remaining = monthlyBudget - totalSpent;
4const daysLeft = getDaysLeftInMonth();
5const dailyBudget = remaining > 0 ? remaining / daysLeft : 0;
6
7// Determine the vibe
8let vibe;
9if (percentage < 50) {
10vibe = { emoji: 'โœจ', message: 'Looking great! You are well under budget.', color: '#A8D8EA' };
11} else if (percentage < 75) {
12vibe = { emoji: '๐Ÿ‘€', message: 'Doing okay! Just keep an eye on it.', color: '#F0D9A0' };
13} else if (percentage < 100) {
14vibe = { emoji: '๐Ÿ˜ฌ', message: 'Getting close to your limit...', color: '#F2A5C0' };
15} else {
16vibe = { emoji: '๐Ÿซฃ', message: 'Over budget! Time to chill on spending.', color: '#F4A7BB' };
17}
18
19return (
20<div className='vibe-check' style={{ borderColor: vibe.color }}>
21<span className='vibe-emoji'>{vibe.emoji}</span>
22<p className='vibe-message'>{vibe.message}</p>
23<div className='budget-details'>
24<p>${totalSpent.toFixed(2)} / ${monthlyBudget.toFixed(2)}</p>
25{remaining > 0 && (
26<p className='daily-budget'>
27~${dailyBudget.toFixed(2)}/day left for {daysLeft} days
28</p>
29)}
30</div>
31
32{/* Progress bar */}
33<div className='progress-track'>
34<div
35className='progress-fill'
36style={{
37width: Math.min(percentage, 100) + '%',
38backgroundColor: vibe.color,
39}}
40/>
41</div>
42</div>
43);
44}
45
46function getDaysLeftInMonth() {
47const now = new Date();
48const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
49return lastDay.getDate() - now.getDate();
50}

Step 5: Recent Expenses List

1function RecentExpenses({ expenses, onDelete }) {
2// Show most recent first
3const sorted = [...expenses].sort(
4(a, b) => new Date(b.date) - new Date(a.date)
5);
6
7return (
8<div className='recent'>
9<h3>Recent</h3>
10{sorted.length === 0 ? (
11<p className='empty'>No expenses yet. Living for free? Teach me.</p>
12) : (
13sorted.slice(0, 10).map(expense => {
14const cat = categories.find(c => c.id === expense.category);
15return (
16<div key={expense.id} className='expense-row'>
17<span className='expense-emoji'>{cat?.emoji}</span>
18<div className='expense-details'>
19<span className='expense-note'>
20{expense.note || cat?.name}
21</span>
22<span className='expense-date'>{expense.date}</span>
23</div>
24<span className='expense-amount'>
25-${expense.amount.toFixed(2)}
26</span>
27<button
28onClick={() => onDelete(expense.id)}
29className='delete-btn'
30>
31x
32</button>
33</div>
34);
35})
36)}
37</div>
38);
39}

๐Ÿ’ก Pro tip

Store your expenses in localStorage (like we did in the self-care tracker) so they persist between sessions. Use the same saveDay/loadDay pattern but keyed by month instead of day.

Level Up Ideas

  • Monthly comparison โ€” see if you're spending more or less than last month
  • Savings goals โ€” set goals ("concert tickets: $200") and track progress
  • Receipt scanner โ€” use your phone camera to auto-fill expense amounts
  • Recurring expenses โ€” auto-add subscriptions on the 1st of every month
  • Export to CSV โ€” download your data for spreadsheet nerds
  • Split expenses โ€” track shared costs with friends and who owes who

๐Ÿ’ฌ Denise says

Building this app teaches you forms, state management, data manipulation, and UI design โ€” basically all the core skills of frontend development. And you end up with an app you'll actually open every day. That's the dream: learning by building things that improve your actual life. Your coding skills AND your bank account will thank you.

๐Ÿ“ธ

Photo coming soon โœจ

๐Ÿš€

Want to keep going?

Tell me what you want to build next and I'll help you write the code.

Start Building โœจ