coinwise
A minimalist, local-first expense tracker for Android.
coinwise is a calm money app that does one thing well: it tells you where your month is going. No budgets to babysit, no ads, no account, no cloud. Your data lives on your device and nowhere else. Built with Expo and React Native, it pairs a deliberately quiet interface with a surprisingly capable engine underneath - recurring bills, multi-currency travel spend, buckets, and multiple languages, all offline-first.
What it is
coinwise tracks your monthly spending without trying to turn you into a budgeting spreadsheet. It’s built around how money actually moves:
- Everyday transactions - log an expense or income in a couple of taps.
- Recurring transactions - rent, subscriptions, salary. Set the cadence once; coinwise projects them forward so your month is always complete.
- Buckets - group one-off spend for a trip or a project, and optionally fold it into (or keep it out of) your monthly total.
It’s local-first and single-device by design: there’s no sign-up, no server, and nothing leaves the phone unless you explicitly export it.
Why it exists
Most expense trackers fail in the same ways: they’re bloated, plastered with ads, gated behind a subscription, or they assume you want to build a strict budget and feel bad when you miss it. They also tend to hold your financial data hostage in someone else’s cloud.
coinwise is a reaction to all of that:
- Calm over busy. A big, honest monthly number and a clean list - not twelve dashboards.
- Private by default. Everything is stored locally in SQLite on the device. No account, no analytics-on-your-spending, no sync server to trust.
- Built for real spending shapes. Recurring bills and bounded events (a holiday, a house move) are first-class, not afterthoughts.
It’s also a personal craft project - a chance to build a genuinely polished, production-shaped mobile app end to end, from data model to design system.
Core features
| Feature | What it does |
|---|---|
| ⚡ Fast capture | Add income or expense through a modal flow with category, note, and date - minimal friction. |
| 🔁 Recurring transactions | Daily / weekly / monthly / yearly cadences with a real occurrence engine. coinwise materializes concrete entries over a rolling 12-month horizon, supports pausing, and normalizes everything to a monthly-equivalent view. |
| 🧺 Buckets | Goal/event buckets with start and end dates and an include-in-month toggle, so a trip’s spend can sit inside or outside your monthly total. |
| 💱 Multi-currency | 30 currencies with live ECB rates (via the free Frankfurter API), cached locally with a 24h TTL so it works offline. The exchange rate is frozen onto each transaction at entry time, keeping historical totals accurate forever. |
| 🌍 4 languages | English, German, French, and Spanish - fully translated across the app, following your device locale with live in-app switching. |
| 🔔 Smart reminders | Local notifications only: gentle rotating spending nudges plus per-recurring due-date heads-ups with a configurable lead time. |
| 🏷️ Custom categories | Build your own categories alongside the sensible defaults. |
| 📤 CSV export | One tap to export your data as CSV and share it out - your data is yours to take. |
| 🌗 Dark mode | System / light / dark with a manual override, applied consistently across navigation, status bar, and the Android navigation bar. |
| 📈 Monthly overview | A hero balance, a period picker, and a spending chart. |
Vision
coinwise stays intentionally small, but there’s room to grow without betraying the “calm and private” core. Directions being explored:
- Optional end-to-end encrypted sync for people with more than one device - opt-in, never required.
- Smarter insights - lightweight trends and category breakdowns that inform without nagging.
- Home-screen widgets for at-a-glance monthly spend.
- iOS support to bring the same experience to more devices.
The guardrail for every future feature: it must keep the app quiet, and it must keep your data yours.
Technical details
Stack
| Layer | Choice |
|---|---|
| Runtime | Expo SDK 56 (managed), React Native 0.85, React 19 |
| Language | TypeScript (strict) |
| Navigation | Expo Router - file-based routes, typed routes, Stack.Protected route guards |
| Styling | NativeWind 5 + Tailwind v4 (token-driven, no inline colors) |
| Database | expo-sqlite (on-device only) |
| ORM | Drizzle ORM - schema-first, typed queries, committed migrations, live queries |
| Forms | react-hook-form + Zod (validation shared by forms and the FX API boundary) |
| Dates | date-fns |
| i18n | i18next + react-i18next + expo-localization |
| Notifications | expo-notifications (local only) |
| Animation | react-native-reanimated 4 + worklets |
| Charts | Skia + wagmi-charts |
| Icons | lucide-react-native |
| Local KV | react-native-mmkv |
| UI bits | @gorhom/bottom-sheet, FlashList |
Engineering decisions
- Local-first, reactive state. State lives close to the data: the UI reacts to Drizzle live queries over SQLite and to MMKV reactive hooks for preferences. No global state library is needed - the database is the source of truth, and the screens simply subscribe to it.
- Money is never a float. All amounts are stored as integer minor units with hand-rolled formatting (locale-correct symbol placement). This sidesteps an entire class of rounding bugs.
- FX snapshotting. When you log a foreign-currency expense, the exchange rate is captured and stored on that transaction. Past months never silently shift when rates change - your history stays truthful.
- Offline-resilient currency. ECB rates are fetched from a free, key-less API, validated with Zod, and cached in MMKV with a TTL, so the app degrades gracefully without a connection.
- Recurring as a reconciler. Rather than computing recurrences on the fly, an engine materializes concrete transaction rows forward and reconciles them on each app open - pruning the future when you edit, respecting pauses, and keeping projections consistent.
- Clean routing/UI split.
app/routes are thin re-exports; all real UI lives underfeatures/<name>/, keeping navigation and screens decoupled. - Design-system driven. Layout and tokens come from companion wireframe and design-system files — colors, radii, and type are tokens, never one-offs.
Get & use coinwise
Install (Android APK)
- Download the latest release: Coinwise APK.
- On your phone, allow installs from your browser/file manager (Settings → Apps → Special access → Install unknown apps).
- Open the APK to install, then launch coinwise.
First run
- Walk through the short onboarding - enter your name, pick a default currency and period-start day, and set your preferences.
- Tap the add button to log your first transaction.
- Add your fixed costs under Recurring, and create a Bucket the next time you take a trip.
Build from source
coinwise is a managed Expo app targeting Android. Because it relies on native modules (MMKV, Skia, Reanimated), run it via a development build, not Expo Go.
git clone https://github.com/elratto21/coinwise
cd coinwise
pnpm install
# Create/run an Android development build
pnpm expo run:android
Uses pnpm for all package management.
coinwise - track your month, keep your data.