I Had a Perfect Budget. It Lasted Three Days.
Last January, I sat down with a spreadsheet and planned every dollar of my monthly budget. Groceries: $400. Dining out: $150. Subscriptions: $50. Transportation: $120. It felt good. Organized. Adult.
By January 4th, I'd already blown past my dining budget because of a friend's birthday dinner I forgot about. By January 15th, I had no idea where I stood. The spreadsheet was abandoned. Sound familiar?
Here's the thing — I tried every budget app out there. Mint, YNAB, Toshl, you name it. They all do one thing well: record what already happened. They're digital receipts. But none of them answered the question that actually matters:
"How far am I drifting from my plan, right now?"
That's why I built DonFlow.
The Real Problem: Tracking ≠ Planning
Most budget apps treat your finances like a rearview mirror. You categorize transactions, see pie charts, maybe get a notification that you overspent. But by then, the money's already gone.
What I wanted was a forward-looking tool:
- Set a budget plan — how much I intend to spend per category
- Import actual spending — from bank exports or manual entry
- See the drift — plan vs. reality, updated in real-time
- Get warned early — before I blow past a category, not after
No existing tool did all four without requiring a monthly subscription, cloud sync, or handing over my bank credentials. So I built one that runs entirely in your browser.
How DonFlow Works
DonFlow is a single-page web app. No server. No sign-up. No data leaves your machine. Everything is stored in IndexedDB, which means your financial data stays exactly where it should — on your device.
The Plan vs. Actual Dashboard
You start by creating a budget plan: categories and monthly amounts. Then you import your actual transactions — paste them, upload a CSV/Excel file, or enter manually. DonFlow overlays them and shows you:
- 🟢 On track — spending within plan
- 🟡 Drifting — approaching the limit
- 🔴 Over budget — you've exceeded the plan
It's simple, visual, and immediate. No waiting for end-of-month reports.
Importing Bank Data with SheetJS
One of the trickiest parts of any finance tool is getting data in. Banks export in wildly different formats — CSV, XLSX, OFX, you name it. DonFlow uses SheetJS to handle the spreadsheet parsing:
import * as XLSX from 'xlsx';
async function parseTransactionFile(file) {
const buffer = await file.arrayBuffer();
const workbook = XLSX.read(buffer, { type: 'array' });
const sheet = workbook.Sheets[workbook.SheetNames[0]];
const rows = XLSX.utils.sheet_to_json(sheet, { header: 1 });
// Auto-detect columns: date, description, amount
const headers = rows[0].map(h => String(h).toLowerCase());
const dateCol = headers.findIndex(h =>
h.includes('date') || h.includes('날짜'));
const amountCol = headers.findIndex(h =>
h.includes('amount') || h.includes('금액'));
return rows.slice(1).map(row => ({
date: row[dateCol],
amount: parseFloat(row[amountCol]),
description: row[headers.findIndex(h =>
h.includes('desc') || h.includes('memo') || h.includes('적요'))]
}));
}
This approach means DonFlow can handle most bank exports without manual column mapping. It auto-detects common header patterns in both English and Korean.
Persisting with IndexedDB
All your plans and transactions live in IndexedDB. No cookies, no localStorage limits, no cloud:
const DB_NAME = 'donflow';
const DB_VERSION = 1;
function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (e) => {
const db = e.target.result;
if (!db.objectStoreNames.contains('transactions')) {
db.createObjectStore('transactions', {
keyPath: 'id', autoIncrement: true
});
}
if (!db.objectStoreNames.contains('budgetPlans')) {
db.createObjectStore('budgetPlans', {
keyPath: 'id', autoIncrement: true
});
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
This means your data survives browser refreshes, and you can use DonFlow offline. Close the tab, come back next week — everything's still there.
Why "No Server" Matters
I'm a developer, so I could've built a full-stack app with user accounts, cloud sync, the works. But I deliberately chose not to. Here's why:
- Privacy by architecture — Your financial data never touches a server I control. There's no database breach risk because there's no database.
- Zero friction — No sign-up form means you can start in literally 5 seconds.
- No recurring costs — I don't need to charge a subscription to cover server bills. It's free, forever.
- Works anywhere — Behind a corporate firewall? On a plane? Doesn't matter. It's just HTML, CSS, and JavaScript.
Try It Right Now
The fastest way to see DonFlow in action is the "Try Demo Data" button. It loads a sample budget plan and three months of transactions so you can immediately see:
- How the plan vs. actual comparison works
- What drift warnings look like
- How categories break down over time
No fake email required. No credit card. Just click and explore.
💡 Want to see how I built DonFlow with zero backend? The full architecture breakdown is in my IndexedDB article — part of my Building a Finance App With No Server series.
What's Next
DonFlow is open source and actively developed. Some things on the roadmap:
- OFX/QFX import — for direct bank statement support
- Multi-currency — because some of us earn in one currency and spend in another
- Budget templates — pre-built plans for common scenarios (student, freelancer, family)
- Export to PDF — shareable monthly reports
The code is on GitHub if you want to contribute, file issues, or just peek under the hood:
🔗 github.com/maxmini0214/donflow
Over to You
I built DonFlow to solve my own frustration with budget apps that only look backward. If you've felt the same pain, give it a spin.
And I'm genuinely curious: What financial data format would you want supported? OFX? QIF? Direct API connections to specific banks? Drop a comment — your answer might shape the next release.
📘 Series: Building a Finance App With No Server → — 5-part deep dive into the architecture.
❤️ Found this useful? A reaction helps more devs discover it!
Top comments (0)