Build a Habit Streak Chrome Extension: Complete Developer Guide
Creating a habit streaks extension is one of the most rewarding Chrome extension projects you can undertake. A daily streak chrome tracker helps users build positive habits by providing visual feedback on their consistency, making it an excellent addition to any productivity toolkit. In this comprehensive guide, we will walk you through building a fully functional habit streaks extension from scratch, covering everything from project setup to advanced features like local storage persistence and badge notifications.
The demand for habit tracking functionality in browser extensions has grown significantly as more people seek digital tools to help them maintain daily routines. Whether you want to track meditation practice, reading goals, exercise routines, or any other daily activity, building a habit streaks extension provides valuable experience with Chrome’s extension APIs while creating something genuinely useful for end users.
This tutorial assumes you have basic familiarity with HTML, CSS, and JavaScript. By the end of this guide, you will have created a complete Chrome extension that tracks daily habits, displays streak counts, and helps users maintain consistency with their goals.
Project Planning and Architecture
Before writing any code, we need to plan our extension’s architecture carefully. A well-designed habit streaks extension consists of several key components that work together seamlessly to provide an excellent user experience.
Core Features Definition
Our habit streaks extension will include the following essential features. First, we need a popup interface that displays the current habit status and streak information. Second, we require local storage to persist habit data across browser sessions. Third, we need badge updates to show quick status information in the toolbar. Fourth, we require daily reset logic to track consecutive days of completion. Fifth, we need streak calculation algorithms to compute and display progress accurately.
The simplicity of a habit streaks extension makes it perfect for learning Chrome extension development while still being useful enough that you will actually want to use it yourself. The core concept is straightforward: users mark a habit as complete for the day, and the extension tracks how many consecutive days they have maintained that habit.
Technology Stack
We will use vanilla JavaScript for this project rather than frameworks like React or Vue. While frameworks can be useful for larger extensions, they add unnecessary complexity for a project of this scope. Our tech stack includes HTML for the popup interface, CSS for styling the extension, and JavaScript for all logic and Chrome API interactions. We will also use Chrome’s storage API for data persistence, which provides a convenient way to store JSON-serializable data that persists across browser sessions.
Extension Manifest Configuration
Every Chrome extension requires a manifest.json file that defines the extension’s properties, permissions, and components. Let us create a comprehensive manifest for our habit streaks extension.
{
"manifest_version": 3,
"name": "Habit Streak Tracker",
"version": "1.0",
"description": "Track your daily habits and maintain consistency streaks",
"permissions": [
"storage",
"alarms",
"notifications"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
This manifest defines several important components. The permissions array includes storage for saving habit data, alarms for scheduling daily reset checks, and notifications for reminding users to complete their habits. The action section configures our popup interface, and the background service worker handles tasks that need to run even when the popup is not open.
Note that we are using Manifest V3, which is the current standard for Chrome extensions. This version includes several improvements over V2, including enhanced security and more efficient background processing.
Building the Popup Interface
The popup is the main user interface for our habit streaks extension. When users click the extension icon in the toolbar, they should see a clean interface that displays their current streak and allows them to mark habits as complete.
HTML Structure
Create a file named popup.html with the following structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Habit Streak Tracker</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<header>
<h1>Habit Streak Tracker</h1>
<p class="subtitle">Build better habits, one day at a time</p>
</header>
<main>
<div class="streak-display">
<div class="streak-number" id="streakCount">0</div>
<div class="streak-label">day streak</div>
</div>
<div class="habit-info">
<p>Last completed: <span id="lastCompleted">Never</span></p>
<p>Best streak: <span id="bestStreak">0</span> days</p>
</div>
<button id="completeBtn" class="complete-button">
Mark Today Complete ✓
</button>
<button id="resetBtn" class="reset-button">
Reset Streak
</button>
</main>
<footer>
<p class="motivational">Consistency is key!</p>
</footer>
</div>
<script src="popup.js"></script>
</body>
</html>
This HTML structure provides a clean, centered layout with a prominent streak display, habit information, and action buttons. The semantic HTML makes it easy to style and accessible for all users.
CSS Styling
Create popup.css to make our extension visually appealing:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
width: 320px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
}
.container {
text-align: center;
}
header {
margin-bottom: 24px;
}
h1 {
font-size: 22px;
font-weight: 600;
margin-bottom: 4px;
}
.subtitle {
font-size: 14px;
opacity: 0.9;
}
.streak-display {
background: rgba(255, 255, 255, 0.2);
border-radius: 16px;
padding: 24px;
margin-bottom: 20px;
backdrop-filter: blur(10px);
}
.streak-number {
font-size: 64px;
font-weight: 700;
line-height: 1;
}
.streak-label {
font-size: 16px;
opacity: 0.9;
margin-top: 8px;
}
.habit-info {
background: rgba(255, 255, 255, 0.15);
border-radius: 12px;
padding: 16px;
margin-bottom: 20px;
text-align: left;
}
.habit-info p {
font-size: 13px;
margin: 8px 0;
opacity: 0.9;
}
.habit-info span {
font-weight: 600;
opacity: 1;
}
.complete-button {
width: 100%;
padding: 14px 24px;
font-size: 16px;
font-weight: 600;
border: none;
border-radius: 12px;
background: #10b981;
color: white;
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 12px;
}
.complete-button:hover {
background: #059669;
transform: translateY(-2px);
}
.complete-button:active {
transform: translateY(0);
}
.complete-button.completed {
background: #6b7280;
cursor: default;
}
.reset-button {
width: 100%;
padding: 10px;
font-size: 13px;
border: none;
border-radius: 8px;
background: rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
transition: background 0.2s;
}
.reset-button:hover {
background: rgba(255, 255, 255, 0.3);
}
footer {
margin-top: 20px;
}
.motivational {
font-size: 12px;
opacity: 0.7;
}
This CSS creates a modern, gradient-themed design with smooth hover effects and clear visual hierarchy. The large streak number serves as the focal point, encouraging users to build higher streaks.
JavaScript Logic Implementation
Now we need to implement the core functionality in popup.js. This file will handle loading and saving habit data, calculating streaks, and updating the UI.
Core Functions
// Constants for storage keys
const STORAGE_KEYS = {
LAST_COMPLETED: 'habit_last_completed',
CURRENT_STREAK: 'habit_current_streak',
BEST_STREAK: 'habit_best_streak'
};
// DOM Elements
const streakCountEl = document.getElementById('streakCount');
const lastCompletedEl = document.getElementById('lastCompleted');
const bestStreakEl = document.getElementById('bestStreak');
const completeBtn = document.getElementById('completeBtn');
const resetBtn = document.getElementById('resetBtn');
// Initialize extension
document.addEventListener('DOMContentLoaded', init);
async function init() {
await loadHabitData();
updateUI();
setupEventListeners();
}
async function loadHabitData() {
return new Promise((resolve) => {
chrome.storage.local.get(
[STORAGE_KEYS.LAST_COMPLETED, STORAGE_KEYS.CURRENT_STREAK, STORAGE_KEYS.BEST_STREAK],
(result) => {
window.habitData = {
lastCompleted: result[STORAGE_KEYS.LAST_COMPLETED] || null,
currentStreak: result[STORAGE_KEYS.CURRENT_STREAK] || 0,
bestStreak: result[STORAGE_KEYS.BEST_STREAK] || 0
};
resolve();
}
);
});
}
function updateUI() {
const { lastCompleted, currentStreak, bestStreak } = window.habitData;
streakCountEl.textContent = currentStreak;
bestStreakEl.textContent = bestStreak;
if (lastCompleted) {
const date = new Date(lastCompleted);
lastCompletedEl.textContent = formatDate(date);
} else {
lastCompletedEl.textContent = 'Never';
}
// Update button state based on whether today is completed
if (isCompletedToday(lastCompleted)) {
completeBtn.textContent = 'Completed Today! ✓';
completeBtn.classList.add('completed');
completeBtn.disabled = true;
}
}
function formatDate(date) {
const today = new Date();
const yesterday = new Date(today);
yesterday.setDate(yesterday.getDate() - 1);
if (isSameDay(date, today)) {
return 'Today';
} else if (isSameDay(date, yesterday)) {
return 'Yesterday';
} else {
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
}
}
function isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}
function isCompletedToday(lastCompleted) {
if (!lastCompleted) return false;
const lastDate = new Date(lastCompleted);
const today = new Date();
return isSameDay(lastDate, today);
}
function setupEventListeners() {
completeBtn.addEventListener('click', handleComplete);
resetBtn.addEventListener('click', handleReset);
}
async function handleComplete() {
const now = new Date();
const { lastCompleted, currentStreak, bestStreak } = window.habitData;
let newStreak = currentStreak;
// Calculate new streak based on last completion
if (lastCompleted) {
const lastDate = new Date(lastCompleted);
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
if (isSameDay(lastDate, now)) {
// Already completed today
return;
} else if (isSameDay(lastDate, yesterday)) {
// Completed yesterday, increment streak
newStreak = currentStreak + 1;
} else {
// Streak broken, reset to 1
newStreak = 1;
}
} else {
// First time completing
newStreak = 1;
}
// Update best streak if necessary
const newBest = Math.max(bestStreak, newStreak);
// Save to storage
await saveHabitData({
lastCompleted: now.toISOString(),
currentStreak: newStreak,
bestStreak: newBest
});
// Update badge
updateBadge(newStreak);
// Update UI
await loadHabitData();
updateUI();
}
async function handleReset() {
if (confirm('Are you sure you want to reset your streak? This cannot be undone.')) {
await saveHabitData({
lastCompleted: null,
currentStreak: 0,
bestStreak: 0
});
updateBadge(0);
await loadHabitData();
updateUI();
}
}
function saveHabitData(data) {
return new Promise((resolve) => {
chrome.storage.local.set({
[STORAGE_KEYS.LAST_COMPLETED]: data.lastCompleted,
[STORAGE_KEYS.CURRENT_STREAK]: data.currentStreak,
[STORAGE_KEYS.BEST_STREAK]: data.bestStreak
}, resolve);
});
}
function updateBadge(streak) {
chrome.action.setBadgeText({ text: streak > 0 ? streak.toString() : '' });
chrome.action.setBadgeBackgroundColor({ color: '#10b981' });
}
This JavaScript implementation handles all the core functionality for our habit streaks extension. The code manages loading and saving data from Chrome’s storage API, calculates streaks based on completion dates, and updates both the popup UI and the extension badge.
Background Service Worker
The background service worker handles tasks that need to run even when the popup is not open. In our case, we will use it to check for streak breaks and potentially send notifications.
// background.js
chrome.alarms.create('checkStreak', { periodInMinutes: 60 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'checkStreak') {
checkAndUpdateStreak();
}
});
function checkAndUpdateStreak() {
chrome.storage.local.get(
['habit_last_completed', 'habit_current_streak'],
(result) => {
const lastCompleted = result.habit_last_completed;
const currentStreak = result.habit_current_streak;
if (lastCompleted) {
const lastDate = new Date(lastCompleted);
const now = new Date();
const yesterday = new Date(now);
yesterday.setDate(yesterday.getDate() - 1);
// If last completed was before yesterday, streak is broken
if (!isSameDay(lastDate, now) && !isSameDay(lastDate, yesterday)) {
chrome.storage.local.set({
habit_current_streak: 0
});
// Update badge
chrome.action.setBadgeText({ text: '' });
}
}
}
);
}
function isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
}
The background service worker checks the streak status hourly and resets it if the user has missed a day. This ensures that the extension accurately reflects the user’s consistency even when they are not actively using it.
Testing Your Extension
Now that we have created all the necessary files, it is time to test our habit streaks extension. Follow these steps to load the extension into Chrome.
Loading the Extension
Open Chrome and navigate to chrome://extensions/. Enable Developer mode by toggling the switch in the top right corner. Click the Load unpacked button and select the folder containing your extension files. Your extension should now appear in the toolbar.
Testing the Features
Click on the extension icon to open the popup. You should see the streak display with zero days and “Never” as the last completed date. Click the “Mark Today Complete” button and observe the streak increase to one day. Refresh the popup and verify that the data persists. Close and reopen Chrome to ensure the data survives browser restarts.
To test streak calculation, manually modify the stored data to simulate completing a habit yesterday. Create a new streak by marking today as complete and verify that it correctly calculates as two days. Then, wait two days without marking completion to test the streak break logic.
Advanced Features to Consider
While our basic habit streaks extension works well, there are many ways to enhance it with additional features that would make it even more useful for users.
Multiple Habit Tracking
You could expand the extension to support tracking multiple habits simultaneously. This would require updating the data structure to store an array of habits, each with its own streak information. The UI would need to allow adding, editing, and deleting habits, and the popup would display a list of all habits with their current streaks.
Notifications and Reminders
Adding notification support would help users remember to complete their daily habits. You could use Chrome’s notifications API to send reminders at a user-configured time each day. The background service worker could check at the designated time and send a notification if the habit has not been completed yet.
Data Export and Sync
Implementing data export would allow users to back up their habit data or analyze it in other tools. You could add a feature to export data as JSON or CSV. For cross-device synchronization, you could implement Chrome’s sync storage API to keep habit data synchronized across all devices where the user is signed in.
Streak Freeze Power-Ups
Many popular habit tracking apps include “streak freeze” features that allow users to maintain their streak even if they miss a day. You could implement a system where users earn streak freezes over time or can purchase them, providing a more forgiving experience for users who occasionally slip up.
Conclusion
Building a habit streaks extension is an excellent project for learning Chrome extension development while creating something genuinely useful. Throughout this guide, we have covered the essential components of a Chrome extension, including the manifest configuration, popup interface with HTML and CSS, JavaScript logic for streak calculation, and background service workers for ongoing tasks.
The habit streaks extension we built demonstrates key concepts that apply to virtually any Chrome extension project. You now understand how to persist data using Chrome’s storage API, how to create engaging user interfaces with popup windows, how to use badges for quick status information, and how to implement background logic with service workers.
This foundation opens the door to building more complex extensions. You could add features like multiple habit tracking, notifications, data synchronization, or even integrate with external APIs. The skills you have developed here transfer directly to any Chrome extension project you tackle in the future.
Remember that the best extensions solve real problems for users. A habit streaks extension addresses a genuine need for people trying to build positive routines, and there is always room for improvement and feature additions. Consider this project as a starting point, and feel free to expand upon it with your own ideas and enhancements.
Start building your habit streaks extension today and help yourself and others maintain consistency in achieving their goals. The sense of accomplishment that comes from watching your streak grow is genuinely motivating, and building the tool yourself makes it even more meaningful.