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 โจ