A modern, responsive expense tracker with a beautiful analytics dashboard, built with React (Vite) + Tailwind CSS on the front end and FastAPI + SQLAlchemy + SQLite on the back end.
- Analytics dashboard — headline stat cards (total spent, this month, average per day, top category), a category donut chart, and a monthly spending bar chart.
- Category-wise spending with an accessible, colorblind-safe palette that reads consistently in light and dark mode.
- Monthly summaries computed server-side from the data.
- Search & filtering — full-text search over title/notes, filter by category and date range, and sort by date / amount / title / category (asc/desc).
- Full CRUD — add, edit, and delete expenses with inline validation and an undoable-feeling confirm dialog for deletes.
- Responsive design — a sidebar layout on desktop that collapses to a mobile drawer; tables become stacked cards on small screens.
- Dark mode — system-aware, one-click toggle, persisted to
localStorage, with no flash of the wrong theme on load. - Toast notifications for every mutation.
- Sample data seeded automatically on first run so the dashboard is never empty.
| Layer | Tech |
|---|---|
| Frontend | React 18, Vite 6, Tailwind CSS 3, React Router, Recharts, lucide-react, date-fns |
| Backend | FastAPI, SQLAlchemy 2, Pydantic 2, Uvicorn |
| Database | SQLite |
expense-tracker/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app + routes
│ │ ├── database.py # SQLAlchemy engine / session
│ │ ├── models.py # Expense ORM model
│ │ ├── schemas.py # Pydantic request/response schemas
│ │ ├── crud.py # Query + mutation logic, summary stats
│ │ └── seed.py # First-run sample data
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── components/ # Layout, charts, table, modal, filters, …
│ │ ├── pages/ # Dashboard, Expenses
│ │ ├── context/ # Theme + toast providers
│ │ └── lib/ # api client, formatting, color palette
│ ├── index.html
│ └── package.json
└── README.md
- Python 3.11+ (developed on 3.14)
- Node.js 18+
cd backend
python -m venv .venv
# Activate the virtualenv
source .venv/Scripts/activate # Git Bash on Windows
# .venv\Scripts\activate # PowerShell / CMD on Windows
# source .venv/bin/activate # macOS / Linux
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
The API is now at http://127.0.0.1:8000 and interactive docs at
http://127.0.0.1:8000/docs. On first run it creates expenses.db and seeds it
with ~90 sample expenses.
In a second terminal:
cd frontend
npm install
npm run dev
Open http://localhost:5173. Vite proxies /api to the backend on port 8000,
so both run happily side by side with no CORS setup.
Base path: /api
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/expenses |
List expenses. Query params: search, category, start_date, end_date, sort_by (date|amount|title|category), order (asc|desc) |
POST |
/expenses |
Create an expense |
GET |
/expenses/{id} |
Get a single expense |
PUT |
/expenses/{id} |
Update an expense |
DELETE |
/expenses/{id} |
Delete an expense |
GET |
/categories |
Distinct category names |
GET |
/stats/summary |
Totals, this-month total, daily average, per-category and per-month breakdowns |
Example expense payload:
{
"title": "Grocery run",
"amount": 42.5,
"category": "Food",
"notes": "Weekly shop",
"date": "2026-07-02T10:30:00"
}
cd frontend
npm run build # outputs to frontend/dist
Serve the built assets behind the same origin as the API (or point a reverse proxy
at both) so the /api calls resolve without CORS.
- The SQLite database (
backend/expenses.db) is git-ignored and regenerated with fresh sample data on first run — delete it to reseed. - Chart colors come from a validated, colorblind-safe categorical palette; identity is never conveyed by color alone (every series is directly labelled).
MIT — do whatever you like.