Build an Email Productivity Chrome Extension: Enhance Gmail and Outlook
Email remains the backbone of professional communication in 2025, with over 4 billion people using email worldwide. For millions of workers, Gmail and Outlook are the primary interfaces for managing their daily correspondence. Building a Chrome extension that enhances email productivity represents a massive opportunity for developers. Whether you want to add email tracking, automate follow-ups, create templates, or integrate with third-party tools, this comprehensive guide will walk you through building a production-ready email productivity extension using Manifest V3.
This tutorial assumes you have basic knowledge of HTML, CSS, and JavaScript. By the end of this guide, you will have created a fully functional email productivity extension that can track emails, display notifications, and integrate with both Gmail and Outlook web interfaces.
Why Build Email Productivity Extensions in 2025?
The demand for email productivity tools continues to grow as professionals seek ways to manage increasing email volumes. Here is why building an email Chrome extension is a lucrative endeavor:
Market Demand
Studies show that the average professional receives approximately 121 emails per day and spends nearly 28% of their workweek reading and responding to emails. Chrome extensions that can reduce this burden have proven market demand. Extensions like Mailtrack, Streak, and Yesware have millions of users, demonstrating that there is substantial opportunity in this space.
Integration Opportunities
Both Gmail and Outlook offer robust web interfaces with extensive developer APIs. These platforms provide hooks for extensions to interact with email content, track interactions, and enhance the user experience. Understanding how to leverage these APIs effectively is a valuable skill that extends beyond email extensions.
Recurring Value
Unlike one-off tools, email productivity extensions become embedded in users’ daily workflows. This creates strong retention and provides opportunities for monetization through premium features, subscriptions, or freemium models.
Project Planning and Architecture
Before writing any code, let us plan the extension architecture. Our email productivity extension will include the following features:
- Email Tracking: Notify users when their emails are opened
- Quick Templates: One-click insertion of pre-written responses
- Follow-up Reminders: Automatic reminders to follow up on unanswered emails
- Email Analytics: Display open rates and response times
- Gmail and Outlook Support: Work seamlessly with both platforms
Technology Stack
- Manifest V3: The latest Chrome extension platform
- Content Scripts: For injecting functionality into email interfaces
- Background Service Workers: For handling tracking pixels and notifications
- Chrome Storage API: For persisting user preferences and data
- Chrome Notifications API: For displaying alerts
Step 1: Creating the Manifest File
Every Chrome extension begins with the manifest.json file. This file defines the extension’s permissions, entry points, and capabilities. Create a new folder for your project and add the following manifest.json:
{
"manifest_version": 3,
"name": "Email Productivity Pro",
"version": "1.0.0",
"description": "Enhance your Gmail and Outlook experience with tracking, templates, and follow-up reminders",
"permissions": [
"storage",
"notifications",
"tabs",
"activeTab",
"scripting"
],
"host_permissions": [
"https://mail.google.com/*",
"https://outlook.live.com/*",
"https://outlook.office.com/*"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"content_scripts": [
{
"matches": ["https://mail.google.com/*", "https://outlook.live.com/*", "https://outlook.office.com/*"],
"js": ["content.js"],
"css": ["styles.css"]
}
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
Understanding Manifest Permissions
The permissions we have defined are essential for our extension’s functionality:
- storage: Saves user preferences, templates, and tracking data
- notifications: Alerts users about email opens and follow-up reminders
- tabs and activeTab: Access information about current browser tabs
- scripting: Inject content scripts into web pages
- host_permissions: Grants access to Gmail and Outlook domains
Step 2: Building the Popup Interface
The popup is what users see when they click the extension icon. It serves as the command center for our extension. Create popup.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Productivity Pro</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="popup-container">
<header>
<h1>Email Productivity Pro</h1>
<span class="status" id="connectionStatus">Connected</span>
</header>
<section class="quick-actions">
<h2>Quick Actions</h2>
<button id="trackEmails" class="action-btn">
<span class="icon">📬</span>
Track New Email
</button>
<button id="insertTemplate" class="action-btn">
<span class="icon">📝</span>
Insert Template
</button>
<button id="setReminder" class="action-btn">
<span class="icon">⏰</span>
Set Follow-up
</button>
</section>
<section class="templates">
<h2>Saved Templates</h2>
<div id="templateList"></div>
<button id="addTemplate" class="secondary-btn">+ Add Template</button>
</section>
<section class="analytics">
<h2>Today's Stats</h2>
<div class="stat">
<span class="stat-label">Emails Tracked</span>
<span class="stat-value" id="trackedCount">0</span>
</div>
<div class="stat">
<span class="stat-label">Opens Detected</span>
<span class="stat-value" id="opensCount">0</span>
</div>
<div class="stat">
<span class="stat-label">Follow-ups Sent</span>
<span class="stat-value" id="followupsCount">0</span>
</div>
</section>
</div>
<script src="popup.js"></script>
</body>
</html>
Now create the corresponding popup.css for styling:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
width: 320px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f8f9fa;
color: #333;
}
.popup-container {
padding: 16px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 1px solid #e0e0e0;
}
h1 {
font-size: 16px;
font-weight: 600;
}
h2 {
font-size: 14px;
font-weight: 600;
margin-bottom: 12px;
color: #666;
}
.status {
font-size: 12px;
padding: 4px 8px;
background-color: #d4edda;
color: #155724;
border-radius: 12px;
}
section {
margin-bottom: 20px;
}
.action-btn {
width: 100%;
padding: 12px;
margin-bottom: 8px;
border: none;
border-radius: 8px;
background-color: #fff;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
cursor: pointer;
display: flex;
align-items: center;
font-size: 14px;
transition: all 0.2s ease;
}
.action-btn:hover {
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
transform: translateY(-1px);
}
.action-btn .icon {
margin-right: 10px;
font-size: 18px;
}
.secondary-btn {
width: 100%;
padding: 10px;
border: 1px dashed #ccc;
border-radius: 8px;
background: transparent;
color: #666;
cursor: pointer;
font-size: 13px;
}
.secondary-btn:hover {
border-color: #999;
color: #333;
}
.stat {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
}
.stat-label {
font-size: 13px;
color: #666;
}
.stat-value {
font-size: 14px;
font-weight: 600;
}
Finally, create popup.js to handle user interactions:
document.addEventListener('DOMContentLoaded', () => {
loadStats();
loadTemplates();
setupEventListeners();
});
function setupEventListeners() {
document.getElementById('trackEmails').addEventListener('click', trackEmail);
document.getElementById('insertTemplate').addEventListener('click', insertTemplate);
document.getElementById('setReminder').addEventListener('click', setReminder);
document.getElementById('addTemplate').addEventListener('click', addTemplate);
}
async function loadStats() {
const stats = await chrome.storage.local.get(['trackedCount', 'opensCount', 'followupsCount']);
document.getElementById('trackedCount').textContent = stats.trackedCount || 0;
document.getElementById('opensCount').textContent = stats.opensCount || 0;
document.getElementById('followupsCount').textContent = stats.followupsCount || 0;
}
async function loadTemplates() {
const { templates = [] } = await chrome.storage.local.get('templates');
const templateList = document.getElementById('templateList');
templateList.innerHTML = templates.map((t, i) => `
<div class="template-item" data-index="${i}">
<span>${t.name}</span>
<button class="delete-btn" data-index="${i}">×</button>
</div>
`).join('');
}
async function trackEmail() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab.url.includes('mail.google.com') && !tab.url.includes('outlook')) {
showNotification('Please open Gmail or Outlook to track an email');
return;
}
chrome.tabs.sendMessage(tab.id, { action: 'enableTracking' });
showNotification('Email tracking enabled');
}
async function insertTemplate() {
const { templates = [] } = await chrome.storage.local.get('templates');
if (templates.length === 0) {
showNotification('No templates saved. Add one first!');
return;
}
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, {
action: 'showTemplatePicker',
templates
});
}
async function setReminder() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.sendMessage(tab.id, { action: 'setReminder' });
}
async function addTemplate() {
const name = prompt('Template name:');
const content = prompt('Template content:');
if (name && content) {
const { templates = [] } = await chrome.storage.local.get('templates');
templates.push({ name, content });
await chrome.storage.local.set({ templates });
loadTemplates();
showNotification('Template added!');
}
}
function showNotification(message) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon48.png',
title: 'Email Productivity Pro',
message: message
});
}
Step 3: Content Scripts for Email Integration
Content scripts are the heart of our extension. They run in the context of web pages and can interact with the DOM to add functionality to Gmail and Outlook. Create content.js:
// Content script for Gmail and Outlook integration
// Track which platform we are on
const platform = detectPlatform();
function detectPlatform() {
const url = window.location.href;
if (url.includes('mail.google.com')) return 'gmail';
if (url.includes('outlook.live.com') || url.includes('outlook.office.com')) return 'outlook';
return 'unknown';
}
// Listen for messages from popup and background
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.action) {
case 'enableTracking':
enableEmailTracking();
break;
case 'showTemplatePicker':
showTemplatePicker(message.templates);
break;
case 'setReminder':
setupFollowUpReminder();
break;
}
});
function enableEmailTracking() {
// Inject tracking pixel into outgoing emails
// This is a simplified version - real implementations use server-side tracking
const composeButton = getComposeButton();
if (composeButton) {
composeButton.addEventListener('click', () => {
setTimeout(() => {
injectTrackingPixel();
}, 2000); // Wait for compose window to open
});
}
showToast('Tracking enabled for new emails');
}
function injectTrackingPixel() {
// Create an invisible 1x1 image that will be embedded in emails
// When the recipient opens the email, this image loads from our server
// We can then track the open event
const trackingUrl = `https://your-tracking-server.com/track?email=${Date.now()}`;
// Store tracking URL for later attachment
window.pendingTrackingUrl = trackingUrl;
console.log('Tracking pixel prepared:', trackingUrl);
}
function getComposeButton() {
if (platform === 'gmail') {
// Gmail's compose button selector
return document.querySelector('[role="button"][gh="cm"]') ||
document.querySelector('.T-I.T-I-KE');
} else if (platform === 'outlook') {
// Outlook's compose button selector
return document.querySelector('[data-toggle="newEmail"]') ||
document.querySelector('.ms-Fabric button');
}
return null;
}
function showTemplatePicker(templates) {
// Create a modal for selecting templates
const modal = document.createElement('div');
modal.className = 'email-pro-modal';
modal.innerHTML = `
<div class="modal-content">
<h3>Select a Template</h3>
<div class="template-list">
${templates.map((t, i) => `
<div class="template-option" data-index="${i}">
<strong>${t.name}</strong>
</div>
`).join('')}
</div>
<button class="close-modal">Close</button>
</div>
`;
document.body.appendChild(modal);
// Add styles
const style = document.createElement('style');
style.textContent = `
.email-pro-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
width: 300px;
}
.template-option {
padding: 10px;
border: 1px solid #ddd;
margin: 5px 0;
border-radius: 4px;
cursor: pointer;
}
.template-option:hover {
background: #f5f5f5;
}
.close-modal {
margin-top: 10px;
padding: 8px 16px;
}
`;
document.head.appendChild(style);
// Handle template selection
modal.querySelectorAll('.template-option').forEach(option => {
option.addEventListener('click', () => {
const index = option.dataset.index;
insertTemplate(templates[index].content);
modal.remove();
});
});
modal.querySelector('.close-modal').addEventListener('click', () => {
modal.remove();
});
}
function insertTemplate(content) {
if (platform === 'gmail') {
// Find the compose textarea and insert content
const composeArea = document.querySelector('[role="textbox"][aria-label="Email body"]') ||
document.querySelector('.Am.Al.editable');
if (composeArea) {
composeArea.focus();
document.execCommand('insertText', false, content);
}
} else if (platform === 'outlook') {
// Outlook compose area
const composeArea = document.querySelector('[role="textbox"]') ||
document.querySelector('.ms-RichTextEditor');
if (composeArea) {
composeArea.focus();
document.execCommand('insertText', false, content);
}
}
showToast('Template inserted!');
}
function setupFollowUpReminder() {
// Save current email context for follow-up reminder
const emailData = getCurrentEmailContext();
if (emailData) {
chrome.storage.local.get(['reminders'], (result) => {
const reminders = result.reminders || [];
reminders.push({
...emailData,
remindAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours from now
});
chrome.storage.local.set({ reminders });
showToast('Follow-up reminder set for tomorrow');
});
}
}
function getCurrentEmailContext() {
// Extract email subject and recipient from current view
if (platform === 'gmail') {
const subject = document.querySelector('.hP')?.textContent;
const recipient = document.querySelector('.gD')?.textContent;
return subject ? { subject, recipient, platform: 'gmail' } : null;
} else if (platform === 'outlook') {
const subject = document.querySelector('[aria-label="Subject"]')?.value;
const recipient = document.querySelector('[aria-label="To"]')?.value;
return subject ? { subject, recipient, platform: 'outlook' } : null;
}
return null;
}
function showToast(message) {
const toast = document.createElement('div');
toast.className = 'email-pro-toast';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
// Initialize platform-specific features
function initializePlatformFeatures() {
if (platform === 'gmail') {
// Add custom Gmail UI elements
injectGmailStyles();
} else if (platform === 'outlook') {
// Add custom Outlook UI elements
injectOutlookStyles();
}
}
function injectGmailStyles() {
const style = document.createElement('style');
style.id = 'email-pro-gmail-styles';
style.textContent = `
.email-pro-toast {
position: fixed;
bottom: 20px;
right: 20px;
background: #333;
color: white;
padding: 12px 20px;
border-radius: 4px;
z-index: 9999;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
`;
document.head.appendChild(style);
}
function injectOutlookStyles() {
const style = document.createElement('style');
style.id = 'email-pro-outlook-styles';
style.textContent = `
.email-pro-toast {
position: fixed;
bottom: 20px;
right: 20px;
background: #0078d4;
color: white;
padding: 12px 20px;
border-radius: 4px;
z-index: 9999;
}
`;
document.head.appendChild(style);
}
// Run initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializePlatformFeatures);
} else {
initializePlatformFeatures();
}
Step 4: Background Service Worker
The background service worker handles tasks that need to run independently of web pages, such as monitoring for email opens and managing follow-up reminders. Create background.js:
// Background service worker for Email Productivity Pro
// Handle installation
chrome.runtime.onInstalled.addListener(() => {
console.log('Email Productivity Pro installed');
// Initialize storage
chrome.storage.local.set({
trackedCount: 0,
opensCount: 0,
followupsCount: 0,
templates: getDefaultTemplates(),
reminders: []
});
});
function getDefaultTemplates() {
return [
{ name: 'Quick Follow-up', content: 'Hi there, just following up on my previous email. Let me know if you have any questions!' },
{ name: 'Thank You', content: 'Thank you for your email. I will get back to you shortly.' },
{ name: 'Meeting Request', content: 'I would love to schedule a meeting to discuss this further. What time works best for you?' }
];
}
// Check for reminders periodically
setInterval(checkReminders, 60000); // Check every minute
async function checkReminders() {
const { reminders = [] } = await chrome.storage.local.get('reminders');
const now = Date.now();
const dueReminders = reminders.filter(r => r.remindAt <= now);
for (const reminder of dueReminders) {
showReminderNotification(reminder);
}
// Clean up past reminders
const remainingReminders = reminders.filter(r => r.remindAt > now);
await chrome.storage.local.set({ reminders: remainingReminders });
}
function showReminderNotification(reminder) {
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon48.png',
title: 'Follow-up Reminder',
message: `Time to follow up on: ${reminder.subject}`
});
// Update stats
const { followupsCount = 0 } = await chrome.storage.local.get('followupsCount');
await chrome.storage.local.set({ followupsCount: followupsCount + 1 });
}
// Handle tracking pixel requests (simplified)
chrome.webRequest.onBeforeRequest.addListener(
(details) => {
if (details.url.includes('/track?')) {
handleEmailOpen(details.url);
}
},
{ urls: ['https://your-tracking-server.com/track*'] }
);
async function handleEmailOpen(trackingUrl) {
// Parse tracking URL to get email ID
const urlParams = new URLSearchParams(trackingUrl.split('?')[1]);
const emailId = urlParams.get('email');
if (emailId) {
// Update open count
const { opensCount = 0 } = await chrome.storage.local.get('opensCount');
await chrome.storage.local.set({ opensCount: opensCount + 1 });
// Log the open event
console.log(`Email ${emailId} opened at ${new Date().toISOString()}`);
}
}
// Handle extension icon click
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.sendMessage(tab.id, { action: 'openPopup' });
});
Step 5: Testing Your Extension
Before publishing, thoroughly test your extension:
- Load unpacked extension: Go to chrome://extensions, enable Developer Mode, and click “Load unpacked”
- Test in Gmail: Open Gmail and verify all features work
- Test in Outlook: Repeat the process for Outlook
- Check console logs: Look for any errors in the background service worker and content script consoles
Common Issues and Solutions
- Content script not loading: Ensure host_permissions in manifest.json include the correct URL patterns
- Storage not persisting: Check that you are using the correct storage API (chrome.storage.local vs chrome.storage.sync)
- Notifications not showing: Verify that the extension has notification permissions
Step 6: Publishing to Chrome Web Store
Once testing is complete, follow these steps to publish:
- Create developer account: Sign up at the Chrome Web Store developer dashboard
- Prepare assets: Create promotional screenshots, a detailed description, and icon files
- Package extension: Use “Pack extension” in chrome://extensions or create a ZIP file
- Submit for review: Upload your packaged extension and submit for review
Store Listing Best Practices
- Use clear, benefit-driven titles
- Write detailed descriptions highlighting key features
- Include high-quality screenshots showing the extension in action
- Choose appropriate categories and tags
Conclusion
Building an email productivity Chrome extension is a rewarding project that can reach millions of users. In this guide, you have learned how to create a Manifest V3 extension with email tracking, templates, and follow-up reminders. The architecture we have built provides a solid foundation for adding more advanced features like email analytics, CRM integration, or AI-powered responses.
Remember that successful extensions solve real problems for users. Continue gathering feedback, iterating on features, and improving the user experience. With dedication and proper execution, your email productivity extension could become an indispensable tool for professionals worldwide.
Happy coding!