Build a Study Timer Chrome Extension: Complete 2025 Guide

23 min read

Build a Study Timer Chrome Extension: Complete 2025 Guide

Build a Study Timer Chrome Extension: Complete 2025 Guide

Are you looking to build a study timer Chrome extension that can help students and professionals stay focused? Perhaps you have struggled with distractions while studying or working, and you want to create a tool that enforces productive habits. Whatever your motivation, building a study timer extension is one of the most rewarding projects you can undertake in 2025.

The demand for study timer extension tools has skyrocketed as more people work and learn from home. A well-designed Pomodoro study Chrome extension can help users manage their time effectively, take strategic breaks, and track their productivity over time. In this comprehensive guide, we will walk you through the entire process of building a feature-rich study timer Chrome extension from scratch.


Why Build a Study Timer Chrome Extension?

Before we dive into the technical details, let us explore why building a study timer extension is an excellent project choice in 2025.

The Market Demand

The productivity app market continues to grow exponentially, with study timers and focus tools leading the charge. Students, remote workers, and professionals are constantly searching for effective ways to manage their time and minimize distractions. A well-crafted focus study extension can fill a significant gap in this market.

Consider these compelling statistics:

  • Over 70% of students report using some form of time management tool while studying
  • Pomodoro technique users report up to 25% improvement in productivity
  • Chrome extensions with timer functionality consistently rank among the top downloaded productivity tools

Learning Opportunities

Building a study timer extension teaches you valuable skills that extend far beyond this single project:

  • State management: Learn how to manage timers, track session data, and handle multiple user interactions simultaneously
  • Local storage: Discover how to persist user preferences and session history using Chrome’s storage APIs
  • Notifications: Implement browser notifications to alert users when breaks or work sessions end
  • Popup and background scripts: Understand the unique architecture of Chrome extensions and how different components communicate

Portfolio Value

A completed study timer extension demonstrates your ability to build practical, user-facing applications. It shows potential employers or clients that you can handle real-world development challenges, from UI design to data persistence.


Project Planning and Feature Set

Before writing any code, let us outline the features our study timer extension will include. A minimum viable product (MVP) should include the following core functionality:

Core Features

  1. Pomodoro Timer: The classic 25-minute work session followed by a 5-minute break
  2. Customizable Durations: Allow users to adjust work and break times to their preference
  3. Visual Countdown: Display remaining time prominently in the popup
  4. Start/Pause/Reset Controls: Full control over the timer state
  5. Notification Alerts: Sound and visual notifications when sessions end

Advanced Features (For This Guide)

  1. Session Tracking: Count completed pomodoros in the current day
  2. Daily Goals: Set and track daily pomodoro targets
  3. Persistent State: Remember timer state if the popup is closed
  4. Statistics Dashboard: Visual representation of study history

Setting Up the Project Structure

Every Chrome extension follows a specific file structure. Let us set up our project correctly.

Required Files

study-timer-extension/
├── manifest.json
├── popup.html
├── popup.js
├── popup.css
├── background.js
├── icons/
│   ├── icon16.png
│   ├── icon48.png
│   └── icon128.png
└── sounds/
    └── notification.mp3

The manifest.json file is the heart of your extension. It tells Chrome about your extension’s capabilities and permissions. Let us create this file first:

{
  "manifest_version": 3,
  "name": "Study Timer Pro",
  "version": "1.0.0",
  "description": "A powerful study timer with Pomodoro technique for better focus",
  "permissions": [
    "storage",
    "notifications",
    "alarms"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  }
}

This manifest uses Manifest V3, which is required for all new Chrome extensions as of 2025. Notice we have included permissions for storage (to save user preferences), notifications (to alert users when sessions end), and alarms (for precise timer functionality).


The popup is what users see when they click your extension icon. It needs to be clean, intuitive, and functional. Let us create the HTML structure:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Study Timer</title>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header>
      <h1>Study Timer Pro</h1>
      <div class="mode-switch">
        <button id="pomodoroBtn" class="mode-btn active">Pomodoro</button>
        <button id="shortBreakBtn" class="mode-btn">Short Break</button>
        <button id="longBreakBtn" class="mode-btn">Long Break</button>
      </div>
    </header>

    <main>
      <div class="timer-display">
        <span id="minutes">25</span>
        <span class="separator">:</span>
        <span id="seconds">00</span>
      </div>
      
      <div class="session-info">
        <span id="sessionType">Focus Time</span>
        <span id="sessionCount">Session #1</span>
      </div>

      <div class="controls">
        <button id="startBtn" class="control-btn primary">Start</button>
        <button id="pauseBtn" class="control-btn secondary">Pause</button>
        <button id="resetBtn" class="control-btn tertiary">Reset</button>
      </div>

      <div class="stats">
        <div class="stat-item">
          <span class="stat-label">Today</span>
          <span id="todayCount" class="stat-value">0</span>
        </div>
        <div class="stat-item">
          <span class="stat-label">Goal</span>
          <span id="dailyGoal" class="stat-value">8</span>
        </div>
      </div>
    </main>

    <footer>
      <button id="settingsBtn" class="settings-toggle">⚙️ Settings</button>
    </footer>
  </div>

  <script src="popup.js"></script>
</body>
</html>

This HTML structure provides a clean interface with mode switching (Pomodoro, Short Break, Long Break), a prominent timer display, control buttons, and basic statistics. The design prioritizes clarity and ease of use.


Styling Your Extension

Now let us add CSS to make our extension visually appealing. Good design matters even for functional tools:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
  width: 320px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  min-height: 450px;
}

.container {
  padding: 20px;
  display: flex;
  flex-direction: column;
  min-height: 450px;
}

header h1 {
  font-size: 18px;
  text-align: center;
  margin-bottom: 16px;
  font-weight: 600;
}

.mode-switch {
  display: flex;
  gap: 4px;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 8px;
  padding: 4px;
  margin-bottom: 24px;
}

.mode-btn {
  flex: 1;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  padding: 8px 4px;
  border-radius: 6px;
  font-size: 11px;
  cursor: pointer;
  transition: all 0.2s ease;
}

.mode-btn.active {
  background: white;
  color: #667eea;
  font-weight: 600;
}

.timer-display {
  font-size: 64px;
  font-weight: 700;
  text-align: center;
  font-variant-numeric: tabular-nums;
  letter-spacing: -2px;
  margin-bottom: 8px;
}

.separator {
  animation: blink 1s infinite;
}

@keyframes blink {
  50% { opacity: 0; }
}

.session-info {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.8);
  margin-bottom: 24px;
  padding: 0 8px;
}

.controls {
  display: flex;
  gap: 8px;
  margin-bottom: 24px;
}

.control-btn {
  flex: 1;
  border: none;
  padding: 12px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: transform 0.1s, box-shadow 0.2s;
}

.control-btn:hover {
  transform: translateY(-1px);
}

.control-btn:active {
  transform: translateY(0);
}

.control-btn.primary {
  background: white;
  color: #667eea;
}

.control-btn.secondary {
  background: rgba(255, 255, 255, 0.2);
  color: white;
}

.control-btn.tertiary {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.8);
}

.stats {
  display: flex;
  gap: 16px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  padding: 16px;
  margin-top: auto;
}

.stat-item {
  flex: 1;
  text-align: center;
}

.stat-label {
  display: block;
  font-size: 11px;
  color: rgba(255, 255, 255, 0.7);
  margin-bottom: 4px;
}

.stat-value {
  font-size: 24px;
  font-weight: 700;
}

footer {
  text-align: center;
  margin-top: 16px;
}

.settings-toggle {
  background: none;
  border: none;
  color: rgba(255, 255, 255, 0.6);
  cursor: pointer;
  font-size: 12px;
}

.settings-toggle:hover {
  color: white;
}

This CSS creates a beautiful gradient background, uses a modern color scheme, and provides visual feedback through hover states and animations. The design is responsive and works well across different screen sizes.


Implementing the Timer Logic

Now comes the core functionality: the timer logic. This is where the magic happens. Let us create the popup.js file:

// Timer state
let timerState = {
  timeRemaining: 25 * 60, // in seconds
  isRunning: false,
  currentMode: 'pomodoro', // pomodoro, shortBreak, longBreak
  sessionCount: 0,
  todayCount: 0,
  today: new Date().toDateString()
};

// Timer durations in minutes
const DURATIONS = {
  pomodoro: 25,
  shortBreak: 5,
  longBreak: 15
};

// DOM elements
const minutesDisplay = document.getElementById('minutes');
const secondsDisplay = document.getElementById('seconds');
const startBtn = document.getElementById('startBtn');
const pauseBtn = document.getElementById('pauseBtn');
const resetBtn = document.getElementById('resetBtn');
const sessionCountDisplay = document.getElementById('sessionCount');
const todayCountDisplay = document.getElementById('todayCount');
const modeButtons = {
  pomodoro: document.getElementById('pomodoroBtn'),
  shortBreak: document.getElementById('shortBreakBtn'),
  longBreak: document.getElementById('longBreakBtn')
};

// Initialize
document.addEventListener('DOMContentLoaded', () => {
  loadState();
  updateDisplay();
  checkNewDay();
});

// Load state from storage
async function loadState() {
  try {
    const stored = await chrome.storage.local.get(['timerState']);
    if (stored.timerState) {
      // Merge stored state with defaults
      timerState = { ...timerState, ...stored.timerState };
    }
    updateDisplay();
  } catch (error) {
    console.error('Error loading state:', error);
  }
}

// Save state to storage
async function saveState() {
  try {
    await chrome.storage.local.set({ timerState });
  } catch (error) {
    console.error('Error saving state:', error);
  }
}

// Check if it's a new day
function checkNewDay() {
  const today = new Date().toDateString();
  if (timerState.today !== today) {
    timerState.today = today;
    timerState.todayCount = 0;
    saveState();
  }
}

// Update the timer display
function updateDisplay() {
  const minutes = Math.floor(timerState.timeRemaining / 60);
  const seconds = timerState.timeRemaining % 60;
  
  minutesDisplay.textContent = minutes.toString().padStart(2, '0');
  secondsDisplay.textContent = seconds.toString().padStart(2, '0');
  
  sessionCountDisplay.textContent = `Session #${timerState.sessionCount + 1}`;
  todayCountDisplay.textContent = timerState.todayCount;
  
  // Update mode buttons
  Object.keys(modeButtons).forEach(mode => {
    modeButtons[mode].classList.toggle('active', mode === timerState.currentMode);
  });
  
  // Update button visibility
  startBtn.style.display = timerState.isRunning ? 'none' : 'block';
  pauseBtn.style.display = timerState.isRunning ? 'block' : 'none';
}

// Start the timer
function startTimer() {
  if (timerState.isRunning) return;
  
  timerState.isRunning = true;
  saveState();
  updateDisplay();
  
  // Use chrome.alarms for reliable timing
  chrome.alarms.create('timerAlarm', { delayInMinutes: 0.017 }); // ~1 second
}

// Pause the timer
function pauseTimer() {
  timerState.isRunning = false;
  saveState();
  updateDisplay();
}

// Reset the timer
function resetTimer() {
  timerState.isRunning = false;
  timerState.timeRemaining = DURATIONS[timerState.currentMode] * 60;
  saveState();
  updateDisplay();
}

// Handle timer completion
function completeSession() {
  timerState.isRunning = false;
  
  // Send notification
  chrome.notifications.create({
    type: 'basic',
    iconUrl: 'icons/icon128.png',
    title: 'Study Timer Pro',
    message: getCompletionMessage()
  });
  
  // Update session count for pomodoro
  if (timerState.currentMode === 'pomodoro') {
    timerState.sessionCount++;
    timerState.todayCount++;
  }
  
  // Switch to next mode
  switchMode(getNextMode());
  saveState();
  updateDisplay();
}

// Get completion message based on mode
function getCompletionMessage() {
  switch (timerState.currentMode) {
    case 'pomodoro':
      return 'Great work! Time for a break.';
    case 'shortBreak':
      return 'Break is over. Ready to focus?';
    case 'longBreak':
      return 'Long break complete. Let\'s get back to work!';
  }
}

// Get the next mode
function getNextMode() {
  if (timerState.currentMode === 'pomodoro') {
    // After 4 pomodoros, take a long break
    return (timerState.sessionCount % 4 === 0) ? 'longBreak' : 'shortBreak';
  }
  return 'pomodoro';
}

// Switch timer mode
function switchMode(mode) {
  timerState.currentMode = mode;
  timerState.timeRemaining = DURATIONS[mode] * 60;
  timerState.isRunning = false;
  updateDisplay();
}

// Event listeners
startBtn.addEventListener('click', startTimer);
pauseBtn.addEventListener('click', pauseTimer);
resetBtn.addEventListener('click', resetTimer);

modeButtons.pomodoro.addEventListener('click', () => switchMode('pomodoro'));
modeButtons.shortBreak.addEventListener('click', () => switchMode('shortBreak'));
modeButtons.longBreak.addEventListener('click', () => switchMode('longBreak'));

// Handle alarm events (for background timing)
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'timerAlarm' && timerState.isRunning) {
    timerState.timeRemaining--;
    
    if (timerState.timeRemaining <= 0) {
      completeSession();
    } else {
      updateDisplay();
      saveState();
    }
  }
});

// Update timer every second when popup is open
setInterval(() => {
  if (timerState.isRunning) {
    timerState.timeRemaining--;
    
    if (timerState.timeRemaining <= 0) {
      completeSession();
    } else {
      updateDisplay();
    }
  }
}, 1000);

This JavaScript file handles all the timer logic, including:

  1. State Management: Properly tracks timer state, session counts, and daily progress
  2. Storage Persistence: Uses Chrome’s storage API to save and restore state
  3. Mode Switching: Allows users to switch between Pomodoro, Short Break, and Long Break modes
  4. Notifications: Sends browser notifications when sessions complete
  5. Background Timing: Uses Chrome alarms to ensure timing accuracy even when the popup is closed

Background Service Worker

For the timer to work reliably even when the popup is closed, we need a background service worker. Create background.js:

// Background service worker for Study Timer Pro

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'studyTimer') {
    chrome.storage.local.get(['timerState'], (result) => {
      const state = result.timerState;
      
      if (state && state.isRunning) {
        state.timeRemaining--;
        
        if (state.timeRemaining <= 0) {
          // Session complete
          if (state.currentMode === 'pomodoro') {
            state.sessionCount++;
            state.todayCount++;
          }
          
          // Send notification
          chrome.notifications.create({
            type: 'basic',
            iconUrl: 'icons/icon128.png',
            title: 'Study Timer Pro',
            message: getCompletionMessage(state.currentMode)
          });
          
          // Switch mode
          state.currentMode = getNextMode(state);
          state.timeRemaining = getDuration(state.currentMode) * 60;
        }
        
        state.isRunning = false;
        chrome.storage.local.set({ timerState: state });
      }
    });
  }
});

function getCompletionMessage(mode) {
  switch (mode) {
    case 'pomodoro':
      return 'Great work! Time for a break.';
    case 'shortBreak':
      return 'Break is over. Ready to focus?';
    case 'longBreak':
      return 'Long break complete. Let\'s get back to work!';
  }
}

function getNextMode(state) {
  if (state.currentMode === 'pomodoro') {
    return (state.sessionCount % 4 === 0) ? 'longBreak' : 'shortBreak';
  }
  return 'pomodoro';
}

function getDuration(mode) {
  const durations = {
    pomodoro: 25,
    shortBreak: 5,
    longBreak: 15
  };
  return durations[mode];
}

The background service worker ensures that your timer continues running accurately even when the user closes the popup. This is crucial for a reliable study timer experience.


Testing Your Extension

Now that we have built all the components, let us test our extension:

  1. Open Chrome and navigate to chrome://extensions/
  2. Enable Developer Mode in the top right corner
  3. Click “Load unpacked” and select your extension folder
  4. Pin the extension to your toolbar for easy access
  5. Test the functionality:
    • Click the extension icon to open the popup
    • Click Start and verify the timer counts down
    • Switch between modes and verify the time updates
    • Let a session complete and verify the notification appears

Enhancing Your Extension

Now that you have a working Pomodoro study Chrome extension, consider adding these advanced features:

1. Statistics and History

Track detailed study history including:

  • Total focus time per day/week/month
  • Session completion rates
  • Most productive hours
  • Streak tracking for consecutive days of study

2. Customization Options

Allow users to customize:

  • Work and break durations
  • Number of sessions before long break
  • Notification sounds
  • Theme colors

3. Integration with External Services

Consider integrating with:

  • Todoist or other task managers
  • Calendar apps for scheduling focus sessions
  • Spotify or other music services for ambient focus music

4. Focus Blocking

Implement website blocking during focus sessions:

  • Block social media and distracting sites
  • Allow whitelisting of educational resources
  • Gradual blocking that increases strictness over time

Publishing Your Extension

Once you have tested your extension thoroughly, you can publish it to the Chrome Web Store:

  1. Prepare your extension:
    • Create icon files (16x16, 48x48, 128x128 pixels)
    • Write a compelling description
    • Take screenshots of your extension in action
  2. Create a developer account:
    • Visit the Chrome Web Store developer dashboard
    • Pay the one-time registration fee ($5)
  3. Upload your extension:
    • Package your extension as a ZIP file
    • Upload through the developer dashboard
    • Fill in all required information
  4. Publish:
    • Submit for review (usually takes a few hours)
    • Once approved, your extension will be live!

Conclusion

Building a study timer Chrome extension is an excellent project that teaches you valuable skills while creating a genuinely useful tool. In this guide, we have covered:

  • Why building a study timer extension is worthwhile
  • How to structure your extension with Manifest V3
  • Creating an intuitive popup interface
  • Styling your extension for a professional look
  • Implementing robust timer logic with state persistence
  • Adding background service workers for reliable timing
  • Testing and preparing for publication

The study timer extension you have built incorporates all the essential features users expect: customizable Pomodoro technique, clear visual feedback, session tracking, and browser notifications. This foundation provides an excellent starting point for adding more advanced features as you continue to develop your extension.

Remember that the best extensions solve real problems. As you use your study timer extension yourself, pay attention to what works well and what could be improved. User feedback—starting with your own—will guide you in making it even better.

Good luck with your extension development journey! If you want to explore more Chrome extension projects, check out our other tutorials on building productivity tools, developer utilities, and more.


This guide is part of the Chrome Extension Guide by theluckystrike — your comprehensive resource for Chrome extension development.

No previous article
No next article