Mint shut down in January 2024 and migrated users to Credit Karma. To build a Mint-style personal finance dashboard in Lovable, use Plaid for bank account connections via Edge Functions, store transactions in Supabase, and display spending by category with charts. Setup takes 45-60 minutes and provides a more customizable experience than Mint ever offered.
Building a Mint-replacement finance dashboard in Lovable
Mint was the gold standard for personal finance apps for 15 years — it aggregated bank accounts, automatically categorized spending, tracked budgets, and showed a single net worth number. When Intuit shut it down in January 2024 and redirected users to Credit Karma (which focuses on credit scores rather than budgeting), millions of users were left without a good alternative. This created a real opportunity: building a custom Mint-like dashboard tailored to your exact workflow.
The technical foundation that made Mint work was bank account aggregation — the ability to connect to thousands of different financial institutions and pull transaction data. Mint used Yodlee under the hood. Today, Plaid is the developer-friendly standard for this same capability. With Plaid, you can connect bank accounts, credit cards, investment accounts, and savings accounts in a single flow, retrieve transaction history with automatic categorization, and check real-time balances. This is exactly what a Mint replacement needs.
Building your own Mint replacement in Lovable has several advantages over Credit Karma or other existing alternatives: you control what data is stored and how it's used, you can customize categories and budgets to match your actual spending patterns, you can add features Mint never had (like per-project budget tracking or custom reports), and you own the entire application. This tutorial walks through building a complete personal finance dashboard with bank connections, transaction categorization, budget tracking, and net worth calculation.
Integration method
Since Mint no longer exists, building a Mint-style personal finance dashboard in Lovable means connecting to bank accounts via Plaid's API through Supabase Edge Functions. Store your Plaid credentials in Cloud Secrets, create Edge Functions for the Link token flow and transaction sync, and build a dashboard with spending categories, budgets, and net worth tracking. All bank data requests run server-side through Edge Functions.
Prerequisites
- A Lovable project with Cloud enabled
- A Plaid developer account — register free at dashboard.plaid.com
- Your Plaid Client ID and Sandbox Secret from the Plaid Dashboard Keys section
- Familiarity with the Plaid Link bank connection flow (see the Plaid integration tutorial for detailed setup steps)
- Basic understanding of personal finance concepts: assets, liabilities, transaction categories
Step-by-step guide
Set up Plaid bank account connections
Set up Plaid bank account connections
A Mint-replacement starts with bank connectivity. Plaid is the industry standard: it supports over 12,000 US financial institutions, provides automatic transaction categorization, and returns real-time balance data. The technical implementation involves three components: the Plaid Link frontend widget (which handles the bank login flow), an Edge Function that creates Link tokens and exchanges public tokens for access tokens, and a Supabase table that stores the access tokens and connection metadata. Go to dashboard.plaid.com and sign in to your developer account. Navigate to Team Settings → Keys and copy your Client ID and Sandbox Secret. You'll need these to store in Cloud Secrets. For a personal finance app, enable these Plaid products when creating your Link tokens: Transactions (for categorized transaction history), Balance (for real-time balance checks), and optionally Identity (to verify account ownership). In the Plaid Dashboard under the Products section, confirm your developer app has access to these products. The Plaid Sandbox lets you test with fake bank accounts without connecting real financial institutions. The test credentials are username: user_good, password: pass_good at 'First Platypus Bank'. This gives you a checking and savings account with pre-populated transaction history for testing all the dashboard features before going to production.
Set up Plaid bank account connection. Create a Supabase Edge Function at supabase/functions/plaid-link/index.ts that handles two actions: action='create-link-token' creates a Plaid link token with products=['transactions','balance'] for the current user ID; action='exchange-token' exchanges the public_token from Plaid Link for an access token and stores it in a plaid_connections table (user_id, item_id, access_token, institution_name, created_at) using service role key. Add a 'Connect Account' button to the dashboard that uses the react-plaid-link package to open the Plaid Link modal.
Paste this in Lovable chat
Pro tip: Store PLAID_CLIENT_ID, PLAID_SECRET, and PLAID_ENV='sandbox' in Cloud Secrets via Cloud → Secrets before creating the Edge Function. Your Edge Function will read these with Deno.env.get().
Expected result: Users can click 'Connect Account' on the dashboard and go through the Plaid Link flow. After connecting a bank, the item_id and institution_name appear in the plaid_connections table.
Sync transactions with automatic categorization
Sync transactions with automatic categorization
Plaid's Transactions product returns each transaction with automatic category labels from a hierarchical taxonomy. For example, a Starbucks purchase might have categories ['Food and Drink', 'Restaurants', 'Coffee Shop']. This built-in categorization is one of the most valuable features for a Mint replacement — it means you don't need to build your own categorization logic for the vast majority of transactions. Create an Edge Function that calls Plaid's /transactions/sync endpoint, which uses a cursor-based approach to return only new, modified, or deleted transactions since the last sync. On first sync, the cursor is empty and Plaid returns all available transaction history (up to 24 months on the Sandbox). Subsequent syncs return only changes. Map Plaid's transaction data to your Supabase transactions table. Store: transaction_id (Plaid's unique ID), user_id, account_id, date, name (merchant name), amount (positive for debits, negative for credits in Plaid's convention), primary_category (first element of Plaid's category array), detailed_category (second element), merchant_name, and pending (boolean for pending transactions). Set up an automatic daily sync using a Supabase scheduled trigger, and also provide a manual 'Sync Now' button in the app. Plaid also sends webhooks when new transactions are available — implementing a webhook receiver Edge Function gives you near-real-time transaction updates.
Create a Supabase Edge Function at supabase/functions/plaid-transactions/index.ts that syncs transactions for a user. It should: fetch the user's plaid_connections from Supabase using service role key, call Plaid /transactions/sync with the stored cursor (empty string for first sync), save new/modified transactions to a Supabase transactions table (columns: transaction_id, user_id, account_id, date, name, amount, primary_category, detailed_category, merchant_name, pending), delete removed transactions, and save the updated cursor back to plaid_connections. Return the count of new transactions synced.
Paste this in Lovable chat
1// supabase/functions/plaid-transactions/index.ts2import { serve } from "https://deno.land/std@0.168.0/http/server.ts";3import { createClient } from "https://esm.sh/@supabase/supabase-js@2";45const PLAID_ENV = Deno.env.get("PLAID_ENV") ?? "sandbox";6const PLAID_BASE = `https://${PLAID_ENV}.plaid.com`;7const CLIENT_ID = Deno.env.get("PLAID_CLIENT_ID") ?? "";8const SECRET = Deno.env.get("PLAID_SECRET") ?? "";9const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" };1011serve(async (req) => {12 if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders });13 const supabase = createClient(Deno.env.get("SUPABASE_URL") ?? "", Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "");1415 try {16 const { user_id } = await req.json();17 const { data: connections } = await supabase.from("plaid_connections").select("access_token, sync_cursor, item_id").eq("user_id", user_id);18 if (!connections?.length) throw new Error("No connected accounts found");1920 let totalAdded = 0;21 for (const conn of connections) {22 let cursor = conn.sync_cursor ?? "";23 let hasMore = true;24 while (hasMore) {25 const res = await fetch(`${PLAID_BASE}/transactions/sync`, {26 method: "POST",27 headers: { "Content-Type": "application/json" },28 body: JSON.stringify({ client_id: CLIENT_ID, secret: SECRET, access_token: conn.access_token, cursor }),29 });30 const data = await res.json();31 if (data.error_code) throw new Error(data.error_message);3233 if (data.added?.length > 0) {34 await supabase.from("transactions").upsert(35 data.added.map((t: any) => ({36 transaction_id: t.transaction_id,37 user_id,38 account_id: t.account_id,39 date: t.date,40 name: t.name,41 amount: t.amount,42 primary_category: t.category?.[0] ?? "Other",43 detailed_category: t.category?.[1] ?? "",44 merchant_name: t.merchant_name ?? "",45 pending: t.pending,46 })),47 { onConflict: "transaction_id" }48 );49 totalAdded += data.added.length;50 }51 if (data.removed?.length > 0) {52 await supabase.from("transactions").delete().in("transaction_id", data.removed.map((r: any) => r.transaction_id));53 }54 cursor = data.next_cursor;55 hasMore = data.has_more;56 await supabase.from("plaid_connections").update({ sync_cursor: cursor }).eq("item_id", conn.item_id);57 }58 }59 return new Response(JSON.stringify({ synced: totalAdded }), { headers: { ...corsHeaders, "Content-Type": "application/json" } });60 } catch (err) {61 return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });62 }63});Pro tip: Plaid's category array has 2-3 levels: ['Food and Drink', 'Restaurants', 'Coffee Shop']. Store the first level as primary_category for grouping in charts, and the second level as detailed_category for transaction detail views. This mirrors how Mint displayed categories.
Expected result: After clicking 'Sync Transactions', the Supabase transactions table is populated with categorized transactions from connected bank accounts. The transaction records include Plaid's category labels.
Build the spending analysis dashboard
Build the spending analysis dashboard
With transactions flowing into Supabase, build the core Mint-like dashboard. The spending analysis view is the most valuable feature — it shows where money is going at a glance. Query the transactions table filtered by the current month and the current user. Group by primary_category and sum the amounts to get category totals. Display these as a donut or pie chart showing category proportions. Show the top 5 categories by spend in a ranked list below the chart. For the transaction list view, display all transactions sorted by date descending with: date, merchant name, category badge, and amount. Amounts should be colored red for debits (positive values in Plaid's convention) and green for credits (negative values — Plaid uses negative to indicate money received). Add filtering by category, date range, and search by merchant name. For budget vs. actual comparison, query the budgets table (from the budgets setup) and join with category totals from transactions to calculate remaining budget and percentage used. A progress bar component from shadcn/ui works well for displaying budget progress. For net worth, fetch all account balances from Plaid's balance endpoint via an Edge Function, sum checking and savings as assets, sum credit card and loan balances as liabilities, and show assets minus liabilities as net worth.
Build a personal finance dashboard with four sections: 1) This Month Summary — total spending this month, total income this month, net cash flow; 2) Spending by Category — donut chart from the transactions table grouped by primary_category for the current month, showing dollar amounts; 3) Recent Transactions — last 20 transactions with date, merchant, category badge, and colored amount; 4) Budget Progress — progress bars for each category comparing actual spend to the budget limit from the budgets table. Add a 'Sync Now' button at the top that calls the plaid-transactions Edge Function.
Paste this in Lovable chat
Pro tip: Plaid reports debits as positive numbers and credits (deposits, transfers in) as negative. In your UI, flip the sign for display: show spending amounts as positive red numbers, and income/deposits as positive green numbers. This matches how Mint and every consumer finance app presents data.
Expected result: The dashboard shows current month spending by category in a chart, a list of recent transactions with categories, and budget progress bars. The Sync button updates data from connected bank accounts.
Add budget creation and monthly comparison
Add budget creation and monthly comparison
Budgets were Mint's most-used feature after account aggregation. Implement budget management with Supabase: a budgets table stores per-category monthly limits, and the dashboard compares actual spending to those limits. Create a Supabase table called budgets with columns: id, user_id, category (text, matching Plaid's primary_category values), monthly_limit (numeric), created_at. Apply RLS so users can only see and modify their own budgets. Build a budget setup page where users can see a list of spending categories (auto-populated from the distinct categories in their transactions table) and set a monthly limit for each. When no budget is set for a category, show the raw spending without a progress bar. For the month-over-month comparison chart, query the transactions table grouped by month and primary_category, returning the last 6 months of data per category. Display as a grouped bar chart showing each category's spending trend. This view — showing that restaurant spending has increased every month for 3 months — is the kind of insight that drives behavior change and made Mint genuinely useful. For users with Credit Karma migrated from Mint, point out in the onboarding that their historical Mint data is available as a CSV export from Credit Karma under Settings → Privacy, and provide a CSV import feature in your app to load historical transaction data.
Add a Budgets page. Create a Supabase budgets table (id, user_id, category, monthly_limit). The Budgets page shows all categories from the user's transactions with an editable monthly limit field. Saving updates the budgets table. On the main dashboard, show budget progress bars using actual current-month spending vs. the budget limit for each category. Add a 6-month trend chart showing spending by category across the last 6 months as a grouped bar chart. Categories with no budget show spending without a limit indicator.
Paste this in Lovable chat
Pro tip: Pre-populate common budget categories when a user sets up their first budgets: Groceries, Restaurants, Entertainment, Transportation, Shopping, Utilities, Healthcare. These map to Plaid's top-level categories and cover 80% of typical spending.
Expected result: Users can set monthly budget limits for spending categories. The dashboard shows progress bars indicating how much of each budget has been used. The 6-month trend chart shows category spending history.
Common use cases
Monthly spending dashboard with category breakdown
A personal finance app automatically categorizes bank and credit card transactions using Plaid's category taxonomy, then displays a monthly spending summary showing how much was spent in each category. Users can view spending trends over time with charts showing current month vs. previous months, and drill down into any category to see individual transactions.
Build a monthly spending dashboard. After bank accounts are connected via Plaid, fetch transactions for the current month using an Edge Function and store them in Supabase with Plaid's category data. Create a dashboard page showing: total spending this month, a donut chart breaking spending into categories (Food, Shopping, Transportation, Entertainment, Utilities, Other), a bar chart comparing spending across the last 6 months, and a transactions list filtered by category when a chart segment is clicked.
Copy this prompt to try it in Lovable
Budget tracker with alerts
A budgeting app lets users set monthly spending limits for each category (e.g., $500 for restaurants, $200 for entertainment) and tracks actual spending against those budgets. When a user is 80% through their budget in any category, the app shows a warning. At month end, a summary shows which budgets were over or under.
Create a budgets page where users can set monthly spending limits per category. Store budgets in a Supabase budgets table with user_id, category, monthly_limit. On the budget page, compare each budget to the current month's actual spending from the transactions table. Show progress bars for each category with color coding: green under 50%, yellow 50-80%, red over 80%. Show a 'You're close to your dining budget' warning when any category exceeds 80% of its limit.
Copy this prompt to try it in Lovable
Net worth tracker across all accounts
A net worth dashboard aggregates balances from all connected accounts — checking, savings, credit cards, and investment accounts — to show a single net worth number (assets minus liabilities). The Edge Function fetches current balances from Plaid for all connected accounts, stores a daily snapshot in Supabase, and the frontend shows a line chart of net worth over time.
Build a net worth tracker that connects to bank and investment accounts via Plaid. Create an Edge Function that fetches current balances for all user-connected Plaid accounts and categorizes them as assets (checking, savings, investment) or liabilities (credit cards, loans). Store a daily snapshot in a net_worth_history table with date and total_net_worth. Display the current net worth prominently, a breakdown by account type, and a 90-day trend line chart.
Copy this prompt to try it in Lovable
Troubleshooting
Transactions show 'Uncategorized' for all items despite Plaid returning category data
Cause: Plaid returns categories as an array (e.g., ['Food and Drink', 'Restaurants']). If your Edge Function stores the array directly and your query expects a string field, the category may display incorrectly or appear empty.
Solution: Access the first element of the categories array with t.category?.[0] for primary_category and t.category?.[1] for detailed_category. Plaid's personal_finance_category object (newer API feature) provides a more detailed taxonomy — if available, use personal_finance_category.primary as the category field. Check the raw transaction JSON in Cloud Logs to confirm which category format your Plaid product returns.
1primary_category: t.personal_finance_category?.primary ?? t.category?.[0] ?? "Other",Charts show duplicate transactions or incorrect totals
Cause: Plaid returns both pending and posted versions of the same transaction. When a pending transaction posts, Plaid sends both a removed event (for the pending transaction ID) and an added event (for the posted transaction ID). If the sync logic doesn't process removed events, you get duplicates.
Solution: Ensure your sync Edge Function processes Plaid's removed transactions: delete rows from the transactions table where transaction_id matches any ID in the data.removed array. Also filter pending transactions from your spending totals by adding WHERE pending = false to your spending queries — pending transactions can shift month boundaries.
1// In your spending query, exclude pending transactions2const { data } = await supabase3 .from('transactions')4 .select('primary_category, amount')5 .eq('user_id', userId)6 .eq('pending', false)7 .gte('date', startOfMonth)8 .lte('date', endOfMonth);Bank connection shows 'Login required' error after initial setup
Cause: Plaid sends an ITEM_LOGIN_REQUIRED webhook when a bank connection becomes invalid due to password changes, expired sessions, or multi-factor authentication requirements. The connected item is temporarily invalid until the user re-authenticates through Plaid Link.
Solution: Implement a Plaid webhook receiver Edge Function to listen for ITEM_LOGIN_REQUIRED events and update the connection status in the plaid_connections table. Show a banner in the app UI when any connection has this status, with a 'Re-connect' button that opens Plaid Link with the existing item to restore the connection without starting from scratch.
Best practices
- Use Plaid's cursor-based /transactions/sync endpoint rather than the older /transactions/get endpoint — it handles transaction modifications and deletions correctly and is significantly more efficient for ongoing syncs.
- Exclude pending transactions from budget calculations and spending totals — pending transactions may not post or may post with different amounts, skewing your numbers.
- Store a copy of all transaction data in Supabase rather than calling Plaid on every page load — Plaid's API is not optimized for high-frequency reads, and the database query is 10-100x faster for dashboard display.
- Apply RLS to the transactions, budgets, and plaid_connections tables so each user can only access their own financial data — financial data is among the most sensitive user information you can store.
- Never expose Plaid access tokens to the frontend — store them in Supabase using the service role key and reference accounts by item_id in all client-side code.
- Implement a data retention policy: offer users the option to delete all their financial data when they close their account, and document your data practices clearly in your privacy policy.
- Test in Plaid's Sandbox before going live — the Sandbox test institution 'First Platypus Bank' with credentials user_good/pass_good provides a realistic transaction set including both checking and credit card accounts.
Alternatives
Plaid is the direct bank connectivity layer for building Mint-style features; this page covers the dashboard design while the Plaid page details the authentication flow mechanics.
QuickBooks provides business-grade expense tracking and invoicing for self-employed users who need more than personal budgeting.
Yodlee is an enterprise-grade financial data aggregator with deeper data history and institutional-grade reliability for apps serving larger user bases.
Frequently asked questions
Why did Mint shut down and what happened to its data?
Intuit shut down Mint in January 2024 and migrated users to Credit Karma, which it also owns. The migration was widely criticized because Credit Karma focuses on credit scores and doesn't offer the same budgeting and spending analysis features that made Mint popular. Former Mint users can export their transaction history as a CSV from Credit Karma under Settings → Privacy → Download your data, and this CSV can be imported into a custom Lovable-built replacement.
Is it safe to connect real bank accounts to a Lovable-built app?
Yes, when implemented correctly. Plaid never exposes your users' bank login credentials to your app — users authenticate directly with Plaid in a hosted flow. Plaid holds SOC 2 Type II certification. Your app receives an access token (not credentials) that you must store encrypted in Supabase. Apply RLS to protect financial data per-user, use the service role key only in Edge Functions, and ensure your privacy policy clearly states what financial data you store and how it's used.
How far back does Plaid retrieve transaction history?
In Sandbox, Plaid provides up to 24 months of synthetic transaction history. In Production, most institutions provide 1-2 years of history on first connection, though this varies by bank. Some institutions provide only 90 days. Transaction history is not backfilled when a user first connects — you get whatever the bank makes available at connection time, then ongoing transactions via the sync cursor.
Can I import my Mint CSV export into this app?
Yes — you can build a CSV import feature that maps Mint's export format (Date, Description, Original Description, Amount, Transaction Type, Category, Account Name, Labels, Notes) to your Supabase transactions table. Ask Lovable: 'Add a CSV import feature for Mint transaction exports. Parse the file and store each row in the transactions table using the existing schema, mapping Mint categories to Plaid category equivalents.'
Does this approach work for international bank accounts?
Plaid supports bank connections in the US, Canada, UK, and parts of Europe. For international users outside these regions, Plaid is not available. Consider Yodlee for broader international coverage, or Tink (Visa-owned) for European Open Banking integrations. The Edge Function architecture and dashboard design in this tutorial apply regardless of which bank data provider you use.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation