RafiFinance — Personal Finance Tracker
I built a complete personal finance app as a single HTML file — no server, no database, no framework. Just one file that runs anywhere and works offline.
I kept losing track of my money. Not in a dramatic way — just the slow, quiet kind where you check your e-wallet and wonder where half of it went. I tried spreadsheets. I tried existing apps. They were either too complex, required an account, or just didn't work the way I think about money.
So I did what any developer does when they can't find the right tool — I built my own.
That constraint turned out to be the most interesting engineering challenge of the whole project.
Most modern web apps have hundreds of files. A React app with a few features easily balloons to 50,000+ files in node_modules. Deploying it requires a build pipeline, a hosting platform, environment variables, and at least 3 configuration files before you write a single line of product code.
RafiFinance v4 is one file. Open it in Chrome, and you have a full finance app. The PWA manifest is embedded inline. The service worker registers from a blob URL. All 3,000+ lines of HTML, CSS, and JS live in one place.
Features
Everything a personal finance tracker needs — and nothing it doesn't.
Here's what RafiFinance looks like in action — running on a real device at finance.rafiarsya.com.
Tap any screenshot to zoom in
All data exports as a single JSON file under the key rfv4. Import it on any device and your full history, budgets, and goals are restored instantly. No account. No sync. No server. Just a file.
Dark theme with #080810 background and #6c5ce7 accent — a deep indigo-purple that felt right for something you look at every day. Inter for UI text, JetBrains Mono for numbers and amounts. Lucide icons from CDN for all iconography.
The Single-File Architecture
How do you fit a full-featured app into one HTML file — cleanly?
A single HTML file can hold everything: markup, styles, and scripts. The trick is keeping it organized when it grows past 3,000 lines. Here's how RafiFinance v4 is structured:
Without React or Vue, I had to manage state manually. The approach was simple: one global state object, one render function per view, and a single save/load cycle to localStorage.
The score (0–100) is computed from three weighted signals:
PWA & Deployment
Making a single HTML file installable as a native-like app — and deploying it in minutes.
A Progressive Web App needs three things: a manifest, a service worker, and HTTPS. Getting all three into a single file took some creativity.
This approach has one limitation: the service worker scope is restricted to the blob URL origin, not the page origin. But for a single-page app that only needs to cache itself, it works perfectly.
The whole deployment is just pushing one file to a GitHub repo connected to Cloudflare Pages. The site is live at finance.rafiarsya.com — a subdomain on my personal domain, pointing to Cloudflare Pages via a CNAME.
What I Learned
Building the simplest possible thing — and still running into real engineering problems.
"Single file" sounds like a limitation. It turned out to be a design philosophy. Every decision had to justify itself against the question: does this need to be here? There's no room for bloat in 3,000 lines. Every feature either earned its place or got cut.
I reached for React by default on every project. This time I deliberately didn't. Vanilla JS with a simple state object, a save() function, and manual DOM updates handled everything RafiFinance needed — without a 200ms hydration delay, without a virtual DOM diffing algorithm, without a build step.
For a personal tool with one user (me), the overhead of a framework was never justified. The right tool for this job was no framework at all.