Skip to main content
RapidDev - Software Development Agency

How to Build a Finance Tracker with Lovable

Build a personal finance tracker in Lovable with income and expense transactions, category-based pie and bar charts, recurring transaction logic, and budget-vs-actual comparisons powered by Supabase database functions. You'll have a full-featured money management app with visual spending insights in about 2.5 hours.

What you'll build

  • Transaction list with income and expense entries, categories, and date filtering
  • Category management with custom icons and colors for both income and expense types
  • Pie chart showing expense breakdown by category using Recharts
  • Bar chart comparing monthly income vs expenses side by side over the last 12 months
  • Budget creation per category per month with progress bars turning red when over budget
  • Recurring transaction engine that auto-generates entries daily via a Supabase scheduled function
  • Budget-vs-actual summary Cards using a Supabase view that aggregates spending per category
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate13 min read2–2.5 hoursLovable Pro or higherApril 2026RapidDev Engineering Team
TL;DR

Build a personal finance tracker in Lovable with income and expense transactions, category-based pie and bar charts, recurring transaction logic, and budget-vs-actual comparisons powered by Supabase database functions. You'll have a full-featured money management app with visual spending insights in about 2.5 hours.

What you're building

A finance tracker has two core data flows: recording money movement and visualizing it. Transactions belong to categories (Food, Rent, Salary, etc.) and each category has a type of income or expense. Budgets are defined per category per month — a budget row stores the limit, and a Supabase view calculates spent_amount by summing transactions in that category for that month.

Recurring transactions are handled with a recurring_transactions table that stores frequency (daily, weekly, monthly) and last_generated_at. A Supabase Edge Function scheduled with pg_cron runs once per day, checks which recurring entries are due, and inserts new transactions automatically. This approach works without any client-side code.

The chart data comes from two Supabase views. The category breakdown view groups transactions by category for a given date range and is used by the pie chart. The monthly summary view groups by year-month and transaction type, powering the bar chart. Both views use SQL aggregation so the frontend only needs simple SELECT queries.

Final result

A working personal finance tracker with transaction recording, category charts, recurring entries, and budget tracking — all synced to Supabase in real time.

Tech stack

LovableFull-stack app generation
SupabaseDatabase, Auth, scheduled functions
RechartsPie and bar charts
shadcn/uiCards, DataTable, Dialogs, Badges
react-hook-form + zodTransaction and budget forms
date-fnsDate formatting and month grouping

Prerequisites

  • Lovable Pro account for multi-table schema generation
  • Supabase project created and URL + anon key saved to Cloud tab → Secrets
  • Basic understanding of income vs expense categorization for your use case
  • Optional: a list of your categories and monthly budget amounts ready to enter

Build steps

1

Set up the financial schema with categories and transactions

Prompt Lovable to create the database schema. Categories are the foundation — every transaction and budget references a category. Getting this schema right saves rewrites later.

prompt.txt
1Build a personal finance tracker. Create these Supabase tables:
2
3- categories: id, user_id, name, type (income|expense), icon (text, emoji), color (hex string), created_at
4- transactions: id, user_id, category_id (FK categories), amount (numeric, positive always), type (income|expense), description, transaction_date (date), is_recurring (bool default false), created_at
5- budgets: id, user_id, category_id (FK categories), month (date, always first day of month e.g. 2024-01-01), budget_amount (numeric), created_at, UNIQUE(user_id, category_id, month)
6- recurring_transactions: id, user_id, category_id (FK categories), amount (numeric), type (income|expense), description, frequency (daily|weekly|monthly), next_due_date (date), is_active (bool default true), created_at
7
8Create two Supabase views:
91. category_spending: user_id, category_id, category_name, category_color, month (first day), total_amount groups transactions by user+category+month
102. monthly_summary: user_id, month, income_total, expense_total, net groups by user+month with conditional sums
11
12RLS: all tables require user_id = auth.uid() for all operations.

Pro tip: Ask Lovable to seed 5 default categories for new users (Food, Transport, Housing, Entertainment, Salary) using a Supabase trigger on auth.users INSERT. This means users see example data immediately after signup.

Expected result: All four tables and two views are created with RLS enabled. The app shell loads with a navigation sidebar and the transaction list page is visible.

2

Build the transaction entry form and list

Create the main transaction interface. The form needs to feel fast to use — users will add transactions frequently. Ask Lovable to build a compact dialog form and a sortable transaction list.

prompt.txt
1Build the transaction management UI:
2
31. Transaction list page at src/pages/Transactions.tsx:
4 - Fetch transactions for the current user with category join, ordered by transaction_date DESC
5 - DataTable columns: Date, Category (Badge with category color), Description, Amount (green for income, red for expense), Actions (edit/delete)
6 - Filter row above table: month picker (Select showing last 12 months), type filter (All/Income/Expense), category Select
7 - Summary row at top: total income (green), total expenses (red), net (positive=green, negative=red)
8
92. Add Transaction Dialog (opened by a floating Add Button):
10 - Form fields: Type toggle (Income/Expense), Category Select (filtered by selected type), Amount Input (numeric), Description Input, Date DatePicker (default today), Is Recurring Switch
11 - If Is Recurring is on, show Frequency Select (Daily/Weekly/Monthly)
12 - On submit: insert into transactions. If recurring, also insert into recurring_transactions with next_due_date = transaction_date + frequency interval
13 - Zod schema validates: amount > 0, date is not in future, category is required

Pro tip: Ask Lovable to add a keyboard shortcut: pressing 'N' anywhere on the page opens the Add Transaction dialog. This makes data entry much faster for daily tracking.

Expected result: The transaction list shows with filters and a summary row. The Add Transaction dialog opens and successfully inserts a new transaction that appears in the list immediately.

3

Add category charts with Recharts

Build the charts page that gives users visual insight into their spending. A pie chart shows where money goes, and a bar chart shows the income-vs-expense trend over time.

src/components/charts/SpendingPieChart.tsx
1// src/components/charts/SpendingPieChart.tsx
2import { PieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer } from 'recharts'
3import { useQuery } from '@tanstack/react-query'
4import { supabase } from '@/integrations/supabase/client'
5
6interface Props {
7 userId: string
8 month: string
9}
10
11export function SpendingPieChart({ userId, month }: Props) {
12 const { data } = useQuery({
13 queryKey: ['category-spending', userId, month],
14 queryFn: async () => {
15 const { data, error } = await supabase
16 .from('category_spending')
17 .select('category_name, category_color, total_amount')
18 .eq('user_id', userId)
19 .eq('month', month)
20 if (error) throw error
21 return data ?? []
22 },
23 })
24
25 if (!data?.length) return <p className="text-muted-foreground text-sm">No expense data for this month.</p>
26
27 return (
28 <ResponsiveContainer width="100%" height={320}>
29 <PieChart>
30 <Pie
31 data={data}
32 dataKey="total_amount"
33 nameKey="category_name"
34 cx="50%"
35 cy="50%"
36 outerRadius={100}
37 label={({ category_name, percent }) =>
38 `${category_name} ${(percent * 100).toFixed(0)}%`
39 }
40 >
41 {data.map((entry, index) => (
42 <Cell key={index} fill={entry.category_color} />
43 ))}
44 </Pie>
45 <Tooltip formatter={(value: number) => `$${value.toFixed(2)}`} />
46 <Legend />
47 </PieChart>
48 </ResponsiveContainer>
49 )
50}

Expected result: The charts page shows a pie chart with colored segments per category and a bar chart with grouped income/expense bars for each month.

4

Build the budget tracking Cards

Ask Lovable to create the budgets page where users set monthly limits per category and see their spending progress. Progress bars change color as users approach or exceed the limit.

prompt.txt
1Build a budgets page at src/pages/Budgets.tsx:
2
31. Budget Cards grid (2 columns on desktop, 1 on mobile):
4 - Each Card shows: category icon + name, month label, budget_amount (limit), spent_amount (from category_spending view), remaining amount
5 - Progress bar: width = (spent_amount / budget_amount) * 100, capped at 100%
6 - Progress bar color: green if < 75%, yellow if 75-99%, red if >= 100%
7 - Show 'Over budget by $X' text in red if spent > budget
8 - Cards sorted by: over-budget first, then by percentage used descending
9
102. Set Budget Dialog (Button on each card or Add Budget Button):
11 - Category Select (expense categories only)
12 - Month picker (Select)
13 - Budget amount Input
14 - On submit: upsert into budgets (update if category+month exists, insert if new)
15
163. Month selector at page top to switch which month's budgets are shown (default: current month)
17
18Query: join budgets with category_spending view on category_id + month to get both budget_amount and spent_amount in one query.

Pro tip: Ask Lovable to add a 'Copy from last month' Button that duplicates all budget amounts from the previous month into the current month. This saves users from re-entering budgets every month.

Expected result: Budget Cards show for all budgeted categories. Progress bars are colored correctly. Setting a new budget via the dialog immediately adds a new Card.

5

Add the recurring transaction Edge Function

Create a Supabase Edge Function that runs on a schedule and auto-generates transactions from recurring entries. This runs server-side so users never have to trigger it manually.

prompt.txt
1Create a Supabase Edge Function at supabase/functions/process-recurring/index.ts that:
2
31. Is triggered by a pg_cron job: SELECT cron.schedule('process-recurring-daily', '0 6 * * *', $$SELECT net.http_post(url:='https://your-project.supabase.co/functions/v1/process-recurring', headers:='{"Authorization": "Bearer YOUR_SERVICE_ROLE_KEY"}'::jsonb) AS request_id$$);
4
52. Function logic:
6 - Use SUPABASE_SERVICE_ROLE_KEY from Deno.env.get()
7 - Query all active recurring_transactions WHERE next_due_date <= today
8 - For each due recurring transaction:
9 a. Insert a new row into transactions (copying category_id, amount, type, description, user_id)
10 b. Set transaction_date = next_due_date
11 c. Update next_due_date: daily = +1 day, weekly = +7 days, monthly = +1 month (use date-fns addDays/addWeeks/addMonths)
12 - Return JSON summary: { processed: number, errors: string[] }
13
143. Add CORS headers and handle OPTIONS preflight

Expected result: The Edge Function is deployed. Calling it manually returns a JSON summary showing how many recurring transactions were processed. The pg_cron job runs it automatically each morning.

Complete code

src/components/charts/MonthlyBarChart.tsx
1import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'
2import { useQuery } from '@tanstack/react-query'
3import { supabase } from '@/integrations/supabase/client'
4import { format, subMonths, startOfMonth } from 'date-fns'
5
6interface MonthlySummary {
7 month: string
8 income_total: number
9 expense_total: number
10 net: number
11}
12
13interface Props {
14 userId: string
15 monthsBack?: number
16}
17
18export function MonthlyBarChart({ userId, monthsBack = 12 }: Props) {
19 const { data, isLoading } = useQuery({
20 queryKey: ['monthly-summary', userId, monthsBack],
21 queryFn: async () => {
22 const since = format(startOfMonth(subMonths(new Date(), monthsBack - 1)), 'yyyy-MM-dd')
23 const { data, error } = await supabase
24 .from('monthly_summary')
25 .select('month, income_total, expense_total, net')
26 .eq('user_id', userId)
27 .gte('month', since)
28 .order('month', { ascending: true })
29 if (error) throw error
30 return (data ?? []).map((row: MonthlySummary) => ({
31 ...row,
32 label: format(new Date(row.month), 'MMM yy'),
33 }))
34 },
35 })
36
37 if (isLoading) return <div className="h-64 animate-pulse bg-muted rounded-lg" />
38 if (!data?.length) return <p className="text-muted-foreground text-sm">No data yet. Add some transactions to see your monthly summary.</p>
39
40 return (
41 <ResponsiveContainer width="100%" height={300}>
42 <BarChart data={data} margin={{ top: 5, right: 20, left: 0, bottom: 5 }}>
43 <CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
44 <XAxis dataKey="label" tick={{ fontSize: 12 }} />
45 <YAxis tickFormatter={(v) => `$${v}`} tick={{ fontSize: 12 }} />
46 <Tooltip formatter={(value: number) => `$${value.toFixed(2)}`} />
47 <Legend />
48 <Bar dataKey="income_total" name="Income" fill="#22c55e" radius={[4, 4, 0, 0]} />
49 <Bar dataKey="expense_total" name="Expenses" fill="#ef4444" radius={[4, 4, 0, 0]} />
50 </BarChart>
51 </ResponsiveContainer>
52 )
53}

Customization ideas

Multi-currency support

Add a currency column to transactions and a default_currency to user profiles. Use a free exchange rate API (like exchangerate-api.com) in an Edge Function to convert all amounts to the user's home currency for chart display. Store amounts in original currency but display converted amounts.

CSV import for bank transactions

Add an import page where users upload a CSV file from their bank. An Edge Function parses the CSV, maps columns to your transaction schema, deduplicates by amount+date+description, and returns a preview for the user to review before confirming the import.

Savings goals progress

Add a savings_goals table with target_amount and target_date. Users can allocate transactions marked as type 'savings' toward specific goals. Show a progress Card per goal with a Recharts RadialBarChart showing how close they are to the target.

Spending alerts via email

Add a notification trigger: when a user's spending in a category exceeds 80% of their budget, send an email via Resend from an Edge Function. Store the alert threshold and last_notified_at per budget row so users aren't spammed with repeated emails.

Shared household tracking

Add a households table and link users to a household. Transactions can be marked as personal or shared. A shared view shows combined household spending split by member, useful for couples or roommates tracking finances together.

Common pitfalls

Pitfall: Storing negative amounts for expenses

How to avoid: Always store amounts as positive numerics. Use the type column (income|expense) to determine the sign when displaying or calculating totals. Your SQL views handle the sign logic: SUM(CASE WHEN type='income' THEN amount ELSE 0 END) AS income_total.

Pitfall: Not setting RLS on the budgets table

How to avoid: Ensure all five tables have RLS enabled with policies that check user_id = auth.uid(). Ask Lovable to verify RLS is enabled by showing the Cloud tab → Database → Tables and confirming the shield icon is green for each table.

Pitfall: Using the client-side date for recurring transaction generation

How to avoid: Use the Supabase Edge Function + pg_cron approach described in Step 5. Server-side scheduling ensures transactions are generated regardless of whether the user is active.

Pitfall: Not indexing transaction_date in the transactions table

How to avoid: Ask Lovable to add: CREATE INDEX idx_transactions_user_date ON transactions(user_id, transaction_date DESC). This index makes month-filtered queries instant even with thousands of transactions.

Best practices

  • Always store financial amounts as numeric (not float) in PostgreSQL. Float types have rounding errors that cause incorrect totals in financial calculations.
  • Use Supabase views for aggregated data (monthly summaries, budget vs actual) rather than computing these in the frontend. Views are faster and keep business logic in the database.
  • Set budget_amount as a positive non-null constraint in the database. Add a check constraint: CHECK (budget_amount > 0). This prevents invalid data without needing client-side validation.
  • Enable Supabase Realtime on the transactions table so the transaction list updates immediately when a new transaction is inserted from the recurring function. This gives users a live feed without polling.
  • Add created_at and updated_at timestamps to every table. For the transactions table, never allow updates — only inserts and deletes. This creates an audit trail of all financial entries.
  • Use date (not timestamp) for transaction_date. Financial transactions belong to a calendar day, not a specific time. Using date avoids timezone confusion when users in different time zones use the app.

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a personal finance tracker with Supabase. I have transactions and budgets tables. Help me write a PostgreSQL view called budget_vs_actual that joins budgets with aggregated transactions grouped by user_id, category_id, and month. The view should return: budget_amount, spent_amount (SUM of transactions for that category+month), remaining_amount (budget_amount - spent_amount), and percentage_used (spent_amount / budget_amount * 100). Handle the case where spent_amount is NULL (no transactions yet) by returning 0.

Lovable Prompt

Add a net worth tracker section to the finance app. Create a new table: accounts (id, user_id, name, account_type: checking|savings|credit|investment|loan, current_balance, currency, created_at). Add a page at /net-worth showing: each account as a Card with balance (red for liabilities like credit/loan, green for assets), total assets sum, total liabilities sum, and net worth (assets - liabilities) in a large prominent number at the top. Add an Edit Balance Dialog per account to update current_balance. Use a Recharts AreaChart to show net worth over time if user logs historical snapshots.

Build Prompt

In Supabase SQL editor, create a function get_spending_trend(p_user_id uuid, p_category_id uuid, p_months int) that returns monthly spending totals for a specific category over the last p_months months. Use generate_series to fill in months with zero spending so the chart has no gaps. Return columns: month (date), total_amount (numeric). Use this function in a Recharts LineChart to show spending trends per category.

Frequently asked questions

Can this finance tracker handle multiple currencies?

Not out of the box with this guide — all amounts are stored in a single currency. To add multi-currency support, add a currency column to transactions and use an Edge Function to fetch exchange rates from a free API. Store amounts in original currency and convert to the user's home currency in the views using the exchange rate. See the customization ideas section for details.

How do I prevent other users from seeing my financial data?

Row Level Security (RLS) on all five tables with policies that check user_id = auth.uid() ensures users can only access their own data. Step 1 includes RLS setup. Verify it's working by checking Cloud tab → Database in Lovable — each table should show RLS as enabled.

Can I import my bank statement history?

Yes, through the CSV import customization idea described in this guide. Most banks export transactions as CSV files. An Edge Function can parse the CSV, deduplicate entries by date+amount+description, and bulk-insert them into the transactions table. Ask Lovable to add the import feature as a follow-up prompt after completing the base build.

How accurate are recurring transactions when using the Edge Function approach?

Very accurate. The pg_cron job runs the Edge Function once per day at 6 AM UTC. Any recurring transaction with a next_due_date on or before today gets created. If the function fails one day (rare), it catches up the next day because it processes all entries where next_due_date <= today, not just today exactly.

What happens to chart data if I delete a transaction?

The Supabase views recalculate automatically on every query, so deleting a transaction immediately removes it from all chart data. There is no cache to invalidate. If you use react-query on the frontend (which Lovable generates by default), the chart components refetch after any mutation and update the UI within seconds.

Can I share the finance tracker with a partner or family member?

The base build is single-user. To add shared access, add a households table, link users to a household, and update all RLS policies to allow access based on household membership. See the 'Shared household tracking' customization idea for the approach. Prompt Lovable to add the household feature after completing the base build.

Is this finance data stored securely in Supabase?

Supabase stores data in PostgreSQL on AWS infrastructure with encryption at rest and in transit. RLS ensures application-level isolation between users. Supabase is SOC 2 Type II certified on the Team plan. For a personal finance tracker, the Free or Pro plan is appropriate. Avoid storing sensitive credentials (bank passwords, card numbers) — this app stores only amounts and categories, not account credentials.

Where can I get help if I need more advanced financial features?

RapidDev builds production Lovable apps including finance tools with multi-currency support, bank integrations, and advanced reporting. Reach out if you need features beyond this guide.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.