Local-first, multi-month expense tracker. Upload bank statements (CSV or PDF), normalize them into a unified schema, merge them into a persistent ledger in localStorage, and explore trends in an interactive dashboard. No backend — all data stays in your browser.
Next.js (App Router) · TypeScript · Tailwind CSS v4 · shadcn-style components · Recharts · PapaParse (CSV) · pdfjs-dist (PDF text extraction)
npm install
npm run dev # http://localhost:3000
npm run build
npm run lint- Persistence — the ledger lives in
localStorageunderexpense_tracker_ledger, managed by a tiny external store (src/lib/ledger.ts) consumed viauseSyncExternalStore, so SSR hydration is safe and all components share one source of truth. - Parsing — src/lib/parsers.ts auto-detects CSV columns (date / description / amount or debit-credit pairs, many date formats) and does best-effort line extraction from PDF statements. Institution is guessed from the file name; categories are assigned by keyword rules (src/lib/categorize.ts) and editable inline afterwards.
- Dedupe — merging keys each row by
date|description|amount; re-uploading the same statement skips duplicates and reports the count. - Dashboard — KPI cards (spend / income / savings rate) and the category donut respect the global month selector; the month-over-month trend line always shows the full dataset for macro context.
interface Transaction {
id: string;
date: string; // YYYY-MM-DD
description: string;
amount: number; // negative = expense, positive = income
category: string;
institution: string; // e.g., Chase, Amex, Wells Fargo
}