Spending Tracker Chrome Extension Guide (2026)
Building a Chrome extension for tracking spending combines web development skills with practical finance management. This guide walks through creating a spending tracker that runs directly in your browser, storing data locally without relying on external servers.
Why Build Your Own Spending Tracker?
Most budget apps require accounts, cloud storage, and constant internet connectivity. A custom Chrome extension gives you complete control over your data. You decide where it lives, how it’s analyzed, and who has access. For developers who value privacy and customization, this approach beats commercial alternatives.
The Chrome extension platform provides several advantages: background scripts for automation, the Storage API for persistent data, and smooth integration with browser UI through popup windows and options pages.
Project Structure
A Chrome extension needs a manifest file and at least one JavaScript file. Here’s a minimal structure for your spending tracker:
spending-tracker/
manifest.json
popup.html
popup.js
background.js
icons/
icon16.png
icon48.png
icon128.png
The manifest defines your extension’s capabilities. Version 3 (MV3) is the current standard with improved security and performance.
Manifest Configuration
Your manifest.json declares permissions and entry points:
{
"manifest_version": 3,
"name": "Spending Tracker",
"version": "1.0",
"description": "Track your spending directly in Chrome",
"permissions": ["storage"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"48": "icons/icon48.png"
}
},
"background": {
"service_worker": "background.js"
}
}
The storage permission enables the chrome.storage API, which syncs data across browser sessions. For purely local storage without sync, use local storage instead.
Core Data Structure
Design your data model before writing logic. A simple transaction schema works well:
const transactionSchema = {
id: string, // UUID
amount: number, // Positive for income, negative for expenses
category: string, // e.g., "food", "transport", "utilities"
description: string,
date: string, // ISO format: "2026-03-15"
timestamp: number // Unix timestamp for sorting
};
Store transactions as an array in chrome.storage:
// Save a new transaction
async function addTransaction(transaction) {
const stored = await chrome.storage.local.get(['transactions']);
const transactions = stored.transactions || [];
transactions.push({
...transaction,
id: crypto.randomUUID(),
timestamp: Date.now()
});
await chrome.storage.local.set({ transactions });
return transactions;
}
Building the Popup Interface
The popup provides quick entry and viewing capabilities. Create popup.html:
<!DOCTYPE html>
<html>
<head>
<style>
body { width: 320px; font-family: system-ui, sans-serif; padding: 16px; }
.input-group { margin-bottom: 12px; }
label { display: block; font-size: 12px; color: #666; margin-bottom: 4px; }
input, select { width: 100%; padding: 8px; box-sizing: border-box; }
button { width: 100%; padding: 10px; background: #4285f4; color: white; border: none; cursor: pointer; }
button:hover { background: #3367d6; }
.recent { margin-top: 16px; border-top: 1px solid #eee; padding-top: 12px; }
.transaction { font-size: 13px; padding: 8px 0; border-bottom: 1px solid #f5f5f5; }
.amount-positive { color: #34a853; }
.amount-negative { color: #ea4335; }
</style>
</head>
<body>
<h3>Add Transaction</h3>
<div class="input-group">
<label>Amount</label>
<input type="number" id="amount" step="0.01" placeholder="0.00">
</div>
<div class="input-group">
<label>Category</label>
<select id="category">
<option value="food">Food</option>
<option value="transport">Transport</option>
<option value="utilities">Utilities</option>
<option value="entertainment">Entertainment</option>
<option value="other">Other</option>
</select>
</div>
<div class="input-group">
<label>Description</label>
<input type="text" id="description" placeholder="What was this for?">
</div>
<button id="saveBtn">Save Transaction</button>
<div class="recent">
<h4>Recent Transactions</h4>
<div id="recentList"></div>
</div>
<script src="popup.js"></script>
</body>
</html>
Popup Logic
Connect the interface to your storage in popup.js:
document.addEventListener('DOMContentLoaded', async () => {
const saveBtn = document.getElementById('saveBtn');
saveBtn.addEventListener('click', async () => {
const amount = parseFloat(document.getElementById('amount').value);
const category = document.getElementById('category').value;
const description = document.getElementById('description').value;
if (!amount || !description) {
alert('Please enter amount and description');
return;
}
const transaction = {
amount: -Math.abs(amount), // Negative for expenses
category,
description,
date: new Date().toISOString().split('T')[0]
};
await addTransaction(transaction);
// Clear form
document.getElementById('amount').value = '';
document.getElementById('description').value = '';
// Refresh list
loadRecentTransactions();
});
async function loadRecentTransactions() {
const stored = await chrome.storage.local.get(['transactions']);
const transactions = (stored.transactions || []).reverse().slice(0, 5);
const list = document.getElementById('recentList');
list.innerHTML = transactions.map(t => `
<div class="transaction">
<span>${t.description}</span>
<span class="${t.amount >= 0 ? 'amount-positive' : 'amount-negative'}">
$${Math.abs(t.amount).toFixed(2)}
</span>
</div>
`).join('');
}
loadRecentTransactions();
});
async function addTransaction(transaction) {
const stored = await chrome.storage.local.get(['transactions']);
const transactions = stored.transactions || [];
transactions.push({
...transaction,
id: crypto.randomUUID(),
timestamp: Date.now()
});
await chrome.storage.local.set({ transactions });
}
Adding Analytics with Background Scripts
Background scripts run continuously, enabling automated insights. In background.js:
chrome.storage.onChanged.addListener((changes, area) => {
if (area === 'local' && changes.transactions) {
const transactions = changes.transactions.newValue;
const summary = calculateSummary(transactions);
// Update badge with daily total
const today = new Date().toISOString().split('T')[0];
const todayTotal = transactions
.filter(t => t.date === today)
.reduce((sum, t) => sum + t.amount, 0);
chrome.action.setBadgeText({
text: Math.abs(todayTotal).toFixed(0)
});
chrome.action.setBadgeBackgroundColor({ color: '#ea4335' });
}
});
function calculateSummary(transactions) {
const byCategory = {};
transactions.forEach(t => {
byCategory[t.category] = (byCategory[t.category] || 0) + t.amount;
});
return byCategory;
}
Loading Your Extension
To test your extension in Chrome:
- Navigate to
chrome://extensions/ - Enable “Developer mode” in the top right
- Click “Load unpacked”
- Select your extension folder
The extension icon appears in your toolbar. Click it to add transactions and see your spending at a glance.
Next Steps for Enhancement
Several features can elevate this foundation. Add an options page for custom categories and budget limits. Implement data export to CSV for spreadsheet analysis. Create charts using a library like Chart.js for visual spending breakdowns. Add keyboard shortcuts for rapid entry without opening the popup.
For developers seeking deeper integration, the Declarative Content API can trigger actions based on browsing behavior, though this requires careful privacy consideration.
This extension keeps your financial data in your browser, under your control. No subscriptions, no data harvesting, just functional expense tracking built exactly to your specifications.
Advanced: Monthly Budget Tracking
Show progress toward a monthly budget with visual progress bars:
function calculateBudgetStatus(transactions, budgets) {
const monthStart = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
const monthlyTotals = transactions
.filter(t => new Date(t.date) >= monthStart)
.reduce((acc, t) => { acc[t.category] = (acc[t.category] || 0) + t.amount; return acc; }, {});
return Object.entries(budgets).map(([category, limit]) => ({
category, spent: monthlyTotals[category] || 0, limit,
pct: ((monthlyTotals[category] || 0) / limit * 100).toFixed(1),
status: (monthlyTotals[category] || 0) >= limit ? 'over' : 'under'
}));
}
Render each category as a progress bar colored green (under 80%), yellow (80-100%), red (over budget).
Comparison with Standalone Budgeting Tools
| Feature | This Extension | YNAB | Mint |
|---|---|---|---|
| Data location | Local (browser) | Cloud | Cloud |
| Bank sync | Not included | Yes | Yes |
| Privacy | Complete local control | Shared | Shared |
| Cost | Free to build | $14.99/month | Free (ads) |
Troubleshooting Common Issues
Storage filling up after months of data: Auto-archive transactions older than 90 days to a JSON file in Downloads, then clear them from storage.
Category totals showing rounding errors: Store amounts in cents (integers) and divide by 100 only when displaying:
// Store: amount_cents: 4250 (for $42.50)
// Display: (transaction.amount_cents / 100).toFixed(2)
CSV not opening correctly in Excel: Add a UTF-8 BOM at the start:
const csvContent = '\uFEFF' + [headers, ...rows].map(r => r.join(',')).join('\n');
This extension keeps your financial data in your browser with no subscriptions and no data harvesting. just functional expense tracking built to your exact specifications.
Try it: Estimate your monthly spend with our Cost Calculator.
Related Reading
- Chrome Extension Word Count Tracker: A Developer Guide
- AI Citation Generator Chrome: A Developer Guide
- AI Color Picker Chrome Extension: A Developer’s Guide
Built by theluckystrike. More at zovo.one
Find the right skill → Browse 155+ skills in our Skill Finder.