Transaction Tracker is a MERN finance tracker app I built in TypeScript for recording, editing and categorising personal transactions. A dashboard turns those records into charts and summaries that update as entries change, custom reports cover a chosen period, and an admin panel manages accounts. It runs on React, Node.js, Express.js and MongoDB, with JWT authentication and bcrypt password hashing.
The problem
Keeping track of money usually starts in a spreadsheet or a notes app, and it usually stops there too. Entries get typed in inconsistently, categories multiply, and the moment you want to know where the money went last month you are building formulas instead of reading an answer.
The other common option, a banking app, only knows about one account and shows a feed of payments without letting you correct, regroup or annotate them. I wanted a tracker where entering a transaction took seconds, fixing a wrong category was a single edit, and the summary on the dashboard reflected that change straight away.
My role
I built Transaction Tracker end to end in TypeScript: the React interface with Tailwind CSS and shadcn/ui, the Node.js and Express.js API, the MongoDB data model for users and transactions, JWT authentication with bcrypt, the dashboard charts, the report generator, and the admin panel for managing accounts. The code is kept on GitHub.
Architecture and tech stack
Transactions as the only source of truth
The app stores individual transactions, each with an amount, a type, a category and a date, and nothing else about money. Totals, category breakdowns and reports are all computed from those records rather than kept as running balances. That costs a little computation, but it means correcting one old entry can never leave a stored total out of step.
Express.js routes shaped around the dashboard
The Express API exposes the usual create, edit and delete routes for transactions, plus summary routes that return the figures the dashboard and reports need already aggregated. The browser receives a handful of numbers for a chart instead of every transaction in the account, which keeps the dashboard quick as a history grows. I took a similar route-first approach to a different domain in the Elentra Car API.
shadcn/ui and Tailwind CSS for dense forms
Most of the time in a finance app is spent in forms and tables. shadcn/ui components give accessible inputs, selects and dialogs that I could restyle with Tailwind CSS, so adding or editing a transaction happens in a dialog over the list without losing your place.
Key features
- Transaction management: add, edit and categorise income and expenses, with the list updating straight away.
- An interactive dashboard with charts and summaries of where money comes from and goes.
- Custom reports for a chosen period, built from the same records as the dashboard.
- An admin panel for overseeing and managing user accounts.
- Profile management for personal details and settings.
- JWT authentication with bcrypt-hashed passwords.
- A responsive interface built with React, Tailwind CSS and shadcn/ui.
Challenges and how I solved them
Charts that follow every edit
- Problem
- When a user changes a transaction's amount or moves it to another category, the list, the summary cards and the charts all have to change together. If only the edited row updates, the dashboard quietly shows numbers that are no longer true.
- Solution
- After any create, edit or delete, the dashboard summaries are fetched again from the API, which recomputes them from the stored transactions. The list, the cards and the charts are all drawn from that fresh response, so they agree with each other and with the database.
Categories that stay useful
- Problem
- Free-text categories fall apart within weeks: "Food", "food" and "Groceries" end up as three slices of the same pie chart, and the breakdown stops meaning anything.
- Solution
- Transactions are categorised by picking from a list rather than typing a label each time, which keeps the grouping consistent and lets the dashboard and reports group spending reliably.
Admin powers in a personal finance app
- Problem
- Someone has to be able to manage accounts, but a person's transactions are private. The admin role needed real authority over users without turning the admin panel into a window onto everyone's spending.
- Solution
- The admin panel is built around account management, and the API checks the role carried in the JWT before any admin route runs. The pattern is close to the role checks in MERN Mail, applied here to protecting financial records instead of outreach logs.
Results
Transaction Tracker gives a single person a clear, editable record of their money: entries go in quickly, mistakes are cheap to fix, and the dashboard and reports always reflect the current data. For a version of the same MERN stack with three roles and a shared workspace rather than private records, see the Team Management System.
What's next
Possible additions include budgets per category and importing transactions from a bank statement file, though neither is a committed plan yet.
