Chrome Extension Habit Tracker for Work: A Developer Guide
Building a habit tracker as a Chrome extension offers unique advantages for work productivity. Unlike standalone apps, a browser-based tracker integrates directly with your workflow, triggering reminders at the right moments and tracking behaviors that happen online. This guide walks you through building a practical habit tracking extension using modern Chrome APIs.
Why Build a Work-Focused Habit Tracker
Browser-based habit tracking fills a gap that general productivity apps miss. Many work habits happen in the browser—checking project management tools, reviewing dashboards, documenting progress. A Chrome extension can detect these behaviors and reinforce positive patterns without requiring manual logging.
The key advantage is context awareness. Your extension knows what tab is active, can monitor specific domains, and can trigger interactions at opportune moments. For developers and power users who spend significant time in the browser, this integration feels natural rather than intrusive.
Core Architecture
A habit tracker extension consists of three primary components:
- Popup Interface — Quick logging and streak viewing
- Background Service — Persistent storage and notifications
- Content Scripts — Optional domain-specific tracking
Here’s the manifest configuration for a minimal habit tracker:
{
"manifest_version": 3,
"name": "Work Habit Tracker",
"version": "1.0",
"permissions": ["storage", "notifications", "activeTab"],
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"background": {
"service_worker": "background.js"
}
}
The storage permission enables Chrome’s sync storage, which automatically backs up habit data to the user’s Google account. This provides cross-device persistence without setting up a backend.
Data Model Design
For a work habit tracker, keep the data model simple but extensible. Each habit needs a unique identifier, name, frequency settings, and historical completion data:
// background.js - Habit data structure
const habitSchema = {
id: 'string',
name: 'string',
frequency: 'daily' | 'weekly',
targetCount: 1,
completions: [], // Array of timestamps
createdAt: Date
};
// Initialize default habits on first install
async function initializeHabits() {
const { habits } = await chrome.storage.sync.get('habits');
if (!habits) {
const defaultHabits = [
{ id: 'code-review', name: 'Complete code review', frequency: 'daily', targetCount: 1, completions: [], createdAt: Date.now() },
{ id: 'update-tickets', name: 'Update project tickets', frequency: 'daily', targetCount: 1, completions: [], createdAt: Date.now() },
{ id: 'break-task', name: 'Take regular breaks', frequency: 'daily', targetCount: 4, completions: [], createdAt: Date.now() }
];
await chrome.storage.sync.set({ habits: defaultHabits });
}
}
This structure stores habits in sync storage, making them available across all Chrome profiles where the user is signed in.
Building the Popup Interface
The popup provides quick access to log habits without leaving your current context. Use vanilla JavaScript to keep the extension lightweight:
<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
<style>
body { width: 300px; padding: 16px; font-family: system-ui; }
.habit-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; }
.habit-name { font-weight: 500; }
.check-btn { background: #4CAF50; border: none; padding: 6px 12px; color: white; border-radius: 4px; cursor: pointer; }
.check-btn.completed { background: #9E9E9E; }
.streak { font-size: 12px; color: #666; }
</style>
</head>
<body>
<h3>Today's Habits</h3>
<div id="habits-list"></div>
<button id="add-habit" style="margin-top: 12px;">+ Add Habit</button>
<script src="popup.js"></script>
</body>
</html>
The corresponding JavaScript loads habits from storage and renders the list with completion buttons:
// popup.js
document.addEventListener('DOMContentLoaded', async () => {
const { habits } = await chrome.storage.sync.get('habits');
const today = new Date().toDateString();
const container = document.getElementById('habits-list');
for (const habit of habits) {
const todayCompleted = habit.completions.some(
ts => new Date(ts).toDateString() === today
);
const habitEl = document.createElement('div');
habitEl.className = 'habit-item';
habitEl.innerHTML = `
<span class="habit-name">${habit.name}</span>
<button class="check-btn ${todayCompleted ? 'completed' : ''}"
data-id="${habit.id}">
${todayCompleted ? '✓' : 'Log'}
</button>
`;
container.appendChild(habitEl);
}
// Handle completion clicks
container.addEventListener('click', async (e) => {
if (e.target.classList.contains('check-btn')) {
await toggleHabitCompletion(e.target.dataset.id);
location.reload();
}
});
});
Implementing Streak Calculations
Streak tracking motivates consistent behavior. Calculate current streaks by checking consecutive days of completion:
// background.js
function calculateStreak(completions) {
if (!completions.length) return 0;
const sorted = completions.map(ts => new Date(ts).toDateString()).sort();
const uniqueDays = [...new Set(sorted)];
let streak = 0;
const today = new Date();
for (let i = uniqueDays.length - 1; i >= 0; i--) {
const day = new Date(uniqueDays[i]);
const expectedDay = new Date(today);
expectedDay.setDate(today.getDate() - (uniqueDays.length - 1 - i));
if (day.toDateString() === expectedDay.toDateString()) {
streak++;
} else {
break;
}
}
return streak;
}
This function returns the current consecutive day count, which you can display in the popup or use to trigger streak-based notifications.
Adding Contextual Notifications
Work habit trackers benefit from smart notifications that don’t disrupt productivity. Use Chrome’s notification API with scheduling:
// background.js
async function scheduleReminder(habitId, hour = 9) {
const now = new Date();
const target = new Date();
target.setHours(hour, 0, 0, 0);
if (target <= now) {
target.setDate(target.getDate() + 1);
}
const delay = target - now;
setTimeout(async () => {
await showHabitReminder(habitId);
scheduleReminder(habitId, hour); // Reschedule for next day
}, delay);
}
async function showHabitReminder(habitId) {
const { habits } = await chrome.storage.sync.get('habits');
const habit = habits.find(h => h.id === habitId);
const today = new Date().toDateString();
const completedToday = habit.completions.some(
ts => new Date(ts).toDateString() === today
);
if (!completedToday) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icon.png',
title: 'Habit Reminder',
message: `Don't forget: ${habit.name}`
});
}
}
Domain-Specific Tracking
For work habits tied to specific websites, content scripts add valuable context:
// manifest.json - add content_scripts
"content_scripts": [{
"matches": ["*://github.com/*"],
"js": ["content-github.js"]
}]
// content-github.js - Track PR reviews
if (window.location.pathname.includes('/pulls')) {
// Track when user views PR list
trackBehavior('viewed-pr-list');
}
This pattern lets you automatically log habits when users visit specific work tools—checking project management boards, reviewing code, or updating documentation.
Extension Storage Considerations
Chrome provides three storage options with different characteristics:
| Storage | Capacity | Sync | Use Case |
|---|---|---|---|
| local | 5MB | No | Large datasets, cache |
| sync | 100KB | Yes | User preferences, habits |
| managed | 5MB | No | Admin-configured settings |
For habit tracking, sync storage works well but has the 100KB limit. At roughly 200 bytes per completion record, you can track thousands of completion events. Prune old data periodically to stay within limits:
async function pruneOldCompletions(habits, keepDays = 90) {
const cutoff = Date.now() - (keepDays * 24 * 60 * 60 * 1000);
return habits.map(habit => ({
...habit,
completions: habit.completions.filter(ts => ts > cutoff)
}));
}
Extension Development Best Practices
When building a work habit tracker, prioritize these practices:
Test with Chrome’s built-in developer tools. Load your extension in developer mode and use the console to debug storage operations and message passing between components.
Handle the case where users don’t grant permissions immediately. Build graceful degradation—your popup should work even without notification permissions.
Respect user attention. Notifications should be informative but never distracting. Use the notificationCenter type for non-urgent reminders that appear in Chrome’s notification center rather than as alerts.
Consider offline behavior. Chrome extensions work offline by default, but sync storage operations queue until connectivity returns. Your UI should indicate when data might be stale.
Extending Your Tracker
Once the core habit tracking works, consider adding features like weekly reports exported as markdown, integration with calendar APIs for meeting-linked habits, or simple gamification with achievement badges stored in sync storage.
The foundation built here—manifest configuration, storage patterns, popup UI, and notifications—provides a template for any productivity-focused Chrome extension. Adapt the data model and interfaces to match specific work flows and team needs.
Related Reading
- Claude Code for Beginners: Complete Getting Started Guide
- Best Claude Skills for Developers in 2026
- Claude Skills Guides Hub
Built by theluckystrike — More at zovo.one