Where your money goes, charted.
A personal finance tracker for Windows, Android and the web: one codebase, synced through the cloud, usable offline, with private and shared ledgers for couples.
- Overview: total spend, budget alerts, upcoming bills, what you're owed, and this financial year's income at a glance.
- Transactions: search and filter expenses by category, payment method or date; track split bills and who still owes you.
- Tap-to-pay capture (Android): optionally reads the payment alerts from Google Wallet and your banking app, and queues each tap for a one-tap review with the amount, merchant, card and a remembered category already filled in.
- Budgets: weekly, fortnightly or monthly limits per category, with progress bars that turn red when you go over.
- Savings goals: targets with deposits and withdrawals, grouped by bank account.
- Investments: holdings with purchase price vs. current value and gain/loss.
- Subscriptions & bills: recurring costs normalised to a monthly figure, an "upcoming" list, and one-tap "log payment".
- Purchases & car expenses: big-ticket items kept separate from everyday spending.
- Income & tax: gross/net/tax/super per payment, financial-year totals, and a PDF export for tax time.
- Banks: a spreadsheet-style pay-split table with your own custom columns.
- Notifications: optional alerts when a bill is due soon or a category nears its budget.
- Private + shared ledgers: every account gets a Personal ledger; invite your partner to a separate shared one, which they join once they accept — and either of you can leave it later.
- Offline-first: opens with your last-synced data and pushes changes when you're back online.
- Backups: export and import everything as a single JSON file.
- Light & dark themes, and 10 currencies.
![]() |
![]() |
| Budgets | Transactions |
On a phone: overview, subscriptions & bills, savings
Screenshots use made-up demo data.
┌──────────────────────┐ ┌──────────────────────────────┐
│ Windows (Electron) │ │ Supabase │
│ Android (Capacitor) │ ─────▶ │ • Auth (email + password) │
│ Web (static host) │ ◀───── │ • Postgres + row-level sec. │
│ same React build │ HTTPS │ │
└──────────────────────┘ └──────────────────────────────┘
local cache for offline
- One codebase. The React app is built once by Vite. The web version is those static files; the Android app is the same files wrapped by Capacitor, and the Windows app is the same files in an Electron window. All three sign in to the same Supabase project, so they share accounts and data.
- No server of your own. Supabase provides login and the database. The whole backend is one SQL file,
supabase/schema.sql. - Ledgers. Each ledger is one JSON document. Row-level security means only a ledger's accepted members can read it, and every write goes through a
save_ledger_statefunction that merges changes instead of overwriting blindly. Sharing is an invitation: being invited grants nothing until you accept, so nobody can push a ledger into your account. - Sync. Changes are saved optimistically and pushed right away. Other devices pick them up within about 15 seconds, or immediately when the app regains focus.
- Native where it matters. On Android, notifications use
@capacitor/local-notifications, and exports (backup JSON, tax PDF) open the system share sheet. On Windows, notifications are Windows toasts and exports open a "Save as" dialog.
- Node.js 20+
- A free Supabase project
- For Android: Android Studio, or the Android SDK plus JDK 21
- For Windows: nothing extra; Electron is installed by
npm install
- Create a project at supabase.com.
- Open SQL Editor, then paste and run
supabase/schema.sql. - Go to Authentication → URL Configuration and set Site URL to where the web app will live (email-confirmation links land there). Or, for a private two-person setup, turn off Confirm email under Authentication → Providers → Email.
cp .env.example .env.localFill in the values from Project Settings → API Keys (the publishable/anon key) and Data API (the project URL):
VITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=sb_publishable_...These are baked in at build time. The anon key is designed to be public; row-level security is what protects the data. Never use the service_role/secret key here.
npm install
npm run dev # http://localhost:5173npm run build # type-checks, then outputs static files to dist/Deploy dist/ to any static host: Netlify, Cloudflare Pages, Vercel, GitHub Pages, etc. public/_redirects gives Netlify and Cloudflare Pages the SPA fallback, so refreshing on /transactions works.
| Command | What it does |
|---|---|
npm run desktop |
Build the web app and open it in an Electron window |
npm run desktop:build |
Installer → release/MoneyMap Setup <version>.exe |
The installer isn't code-signed, so Windows SmartScreen shows "Windows protected your PC" on first run; choose More info → Run anyway. Bump version in package.json before building an update; installing the new setup over the old one keeps your data.
| Command | What it does |
|---|---|
npm run android:sync |
Build the web app and copy it into android/ |
npm run android:open |
Open the project in Android Studio (run on a device/emulator) |
npm run android:apk |
Debug APK → android/app/build/outputs/apk/debug/app-debug.apk |
npm run android:release |
Signed release APK → android/app/build/outputs/apk/release/app-release.apk |
Re-run android:sync (or either APK command) after any web change.
Release builds are signed using android/keystore.properties, which is gitignored, as are all *.jks/*.keystore files. To set up signing on a new machine:
keytool -genkeypair -v -keystore android/moneymap-release.jks -storetype PKCS12 \
-alias moneymap -keyalg RSA -keysize 4096 -validity 10000Then create android/keystore.properties:
storeFile=../moneymap-release.jks
storePassword=your-store-password
keyAlias=moneymap
keyPassword=your-key-passwordWithout this file, android:release still builds, but produces an unsigned APK.
Back up the keystore and its password somewhere safe. Android only accepts updates signed with the same key; if you lose it, you can't update the installed app and users must uninstall and reinstall.
node scripts/generate-icons.mjs renders the logo into the PWA icons and the Android launcher icons and splash screens. It has no dependencies.
src/
components/ Shared UI: layout shell, sidebar, auth/profile/connection gates, form fields
features/<name>/ One folder per section: page, cards and forms
lib/ Domain types and pure logic (budgets, due dates, financial year, currency),
plus the Supabase client (api.ts, supabase.ts) and native bridges
(notify.ts, saveFile.ts)
store/ Zustand stores: finance data + sync, auth, ledgers, theme, notifications
supabase/
schema.sql The entire backend: tables, row-level security, RPC functions
android/ Capacitor Android project
electron/ Windows app: the Electron main process
scripts/ Icon generator
public/ PWA manifest, service worker, icons
React 19 · TypeScript · Vite · Tailwind CSS v4 · Zustand · Framer Motion · Recharts · lucide-react · jsPDF · Supabase · Capacitor 8 · Electron
A dark "chart room" palette (deep ink, verdigris accents, one warm parchment hero panel) with a wayfinding motif: a marker travels along budget and savings progress bars, because this is, literally, a map of where your money goes. The token system lives in src/index.css.
If MoneyMap is useful to you, you can support its development on Ko-fi: ko-fi.com/crystaxit.
MIT © 2026 Sarvesh Pandit



