Project Setup and Architecture
Module 07 is a guided project build. You will apply every concept from the previous six modules to build and deploy ZeroBudget — a complete zero-based budgeting app. Start here before writing a single component.
What you are building
Section titled “What you are building”ZeroBudget implements the every-dollar budgeting method: every dollar of income is assigned to a spending category before the month begins. The app tracks:
- Income sources — multiple sources with names and amounts
- Budget categories — 8 preset categories with user-editable spending limits
- Left to assign — real-time difference between total income and total budgeted
- Transactions — individual purchases logged to a category
- Per-category stats — spent, remaining, and a color-coded progress bar
- Month navigation — independent data per month with copy-forward and reset
You can explore the finished app and read the source before you build it.
Scaffold the project
Section titled “Scaffold the project”npm create vite@latest zero-budget -- --template reactcd zero-budgetnpm installCreate the folder structure:
src/├── components/│ ├── MonthNav/│ ├── IncomeSection/│ ├── LeftToAssign/│ ├── CategoryCard/│ ├── TransactionForm/│ └── TransactionList/├── context/│ └── BudgetContext.jsx├── data/│ └── defaults.js├── hooks/│ ├── useBudget.js│ └── useLocalStorage.js├── App.css├── App.jsx├── index.css└── main.jsxThe data model
Section titled “The data model”Three arrays drive the entire app:
// Income source{ id: 'uuid', name: 'Paycheck', amount: 3500 }
// Category{ id: 'housing', name: 'Housing', budgeted: 1200 }
// Transaction{ id: 'uuid', categoryId: 'housing', amount: 950, description: 'Rent', date: '2026-06-01' }All three live together in a month object stored in localStorage:
{ incomeSources: [...], categories: [...], transactions: [...],}The localStorage key is zb-YYYY-MM — one entry per month.
Derived values
Section titled “Derived values”These are computed from the arrays, not stored separately:
| Value | Formula |
|---|---|
totalIncome | incomeSources.reduce((sum, s) => sum + s.amount, 0) |
totalBudgeted | categories.reduce((sum, c) => sum + c.budgeted, 0) |
leftToAssign | totalIncome - totalBudgeted |
spentByCategory | transactions.reduce(...) keyed by categoryId |
spent (per card) | spentByCategory[category.id] || 0 |
remaining (per card) | category.budgeted - spent |
pct (per card) | (spent / category.budgeted) * 100 |
Architecture decisions
Section titled “Architecture decisions”useBudgetowns all state and computes all derived values.BudgetContextbroadcasts the hook’s return value to all components.- Components read from context — no prop drilling through
App. Appis a layout component with local state only for the new-category form input.
Exercise
Section titled “Exercise”- Scaffold the project with the command above.
- Create all the directories listed in the folder structure.
- Create
src/data/defaults.jswith the 8 default categories: Housing, Transportation, Food, Utilities, Clothing, Personal, Savings, Giving. Each should have a uniqueidstring, aname, andbudgeted: 0. - Create an empty
useLocalStorage.jsinsrc/hooks/and write the hook from Module 04, Lesson 03.
- ZeroBudget stores income sources, categories, and transactions together in a month-keyed localStorage object.
- All totals and per-category stats are derived from those three arrays — not stored separately.
- The architecture:
useBudgetowns data →BudgetContextbroadcasts it → components read it directly. - Build structure first, features second. Create every folder and every placeholder file before writing logic.