Build a Budget Bestie App
A cute expense tracker that actually makes you want to manage your money. No boring spreadsheets allowed.
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 emojis2const 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];1213// 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';23function AddExpense({ onAdd }) {4const [amount, setAmount] = useState('');5const [category, setCategory] = useState('food');6const [note, setNote] = useState('');78const handleSubmit = (e) => {9e.preventDefault();10if (!amount) return;1112onAdd({13id: Date.now(),14amount: parseFloat(amount),15category,16note,17date: new Date().toISOString().split('T')[0],18});1920// Reset form21setAmount('');22setNote('');23};2425return (26<form onSubmit={handleSubmit} className='add-expense'>27<h3>Add Expense</h3>2829{/* Amount input */}30<div className='amount-input'>31<span className='dollar-sign'>$</span>32<input33type='number'34step='0.01'35placeholder='0.00'36value={amount}37onChange={e => setAmount(e.target.value)}38/>39</div>4041{/* Category picker — emoji buttons */}42<div className='category-grid'>43{categories.map(cat => (44<button45key={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>5657{/* Note */}58<input59type='text'60placeholder='What was it for? (optional)'61value={note}62onChange={e => setNote(e.target.value)}63className='note-input'64/>6566<button type='submit' className='submit-btn'>67Add Expense68</button>69</form>70);71}
Step 3: Spending Summary
1function SpendingSummary({ expenses, month }) {2// Total spent this month3const total = expenses.reduce((sum, e) => sum + e.amount, 0);45// Spending by category6const 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;1011return {12...cat,13total: catTotal,14percentage: Math.round(percentage),15count: catExpenses.length,16};17}).filter(cat => cat.total > 0); // Only show categories with spending1819return (20<div className='summary'>21<h3>This Month</h3>22<p className='total-amount'>${total.toFixed(2)}</p>2324{/* Visual bar breakdown */}25<div className='spending-bar'>26{byCategory.map(cat => (27<div28key={cat.id}29className='bar-segment'30style={{31width: cat.percentage + '%',32backgroundColor: cat.color,33}}34title={cat.name + ': $' + cat.total.toFixed(2)}35/>36))}37</div>3839{/* Category breakdown list */}40<div className='category-list'>41{byCategory42.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;67// Determine the vibe8let 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}1819return (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} days28</p>29)}30</div>3132{/* Progress bar */}33<div className='progress-track'>34<div35className='progress-fill'36style={{37width: Math.min(percentage, 100) + '%',38backgroundColor: vibe.color,39}}40/>41</div>42</div>43);44}4546function 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 first3const sorted = [...expenses].sort(4(a, b) => new Date(b.date) - new Date(a.date)5);67return (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<button28onClick={() => onDelete(expense.id)}29className='delete-btn'30>31x32</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 ✨