Build a Smart Tab Grouper Chrome Extension: Complete Developer's Guide

24 min read

Build a Smart Tab Grouper Chrome Extension: Complete Developer's Guide

Build a Smart Tab Grouper Chrome Extension: Complete Developer’s Guide

Do you ever find yourself drowning in dozens of open Chrome tabs, struggling to find that one website you opened hours ago? If so, you’re not alone. The average power browser has anywhere from 20 to 100 tabs open at any given time, and manually organizing them is a tedious task that most of us avoid until it becomes absolutely necessary. This is where a tab grouper extension becomes invaluable.

In this comprehensive tutorial, we’ll walk through building a smart tab grouper Chrome extension that automatically organizes your tabs based on domain, content type, or custom rules. Whether you’re a beginner looking to learn Chrome extension development or an experienced developer wanting to add a powerful tool to your portfolio, this guide has everything you need.


Why Build a Tab Grouper Extension?

Before diving into the code, let’s understand why tab grouper extensions are so popular and valuable. Chrome’s built-in tab groups feature is powerful, but it requires manual organization. A smart tab grouper extension can automatically:

  • Auto group tabs based on their domain or URL patterns
  • Color-code tabs by category for quick visual identification
  • Create custom rules for specific use cases
  • Save and restore tab groups across sessions
  • Detect duplicate tabs and consolidate them
  • Suspend inactive tabs to save memory

The demand for tools that help organize tabs Chrome has never been higher. With remote work becoming the norm and users juggling multiple projects simultaneously, a well-built tab grouper extension can attract thousands of users and potentially generate revenue through premium features or donations.


Project Setup and Prerequisites

Before we start coding, let’s set up our development environment. You’ll need:

  1. Google Chrome or any Chromium-based browser
  2. A code editor (VS Code is recommended)
  3. Basic knowledge of HTML, CSS, and JavaScript
  4. Node.js (optional, but helpful for development)

Creating the Project Structure

Create a new folder for your extension and set up the following structure:

tab-grouper/
├── manifest.json
├── popup.html
├── popup.js
├── popup.css
├── background.js
├── content.js
├── options.html
├── options.js
├── rules.json
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

The Manifest File: Your Extension’s Foundation

Every Chrome extension starts with a manifest.json file. This configuration file tells Chrome about your extension’s permissions, files, and capabilities. For our tab grouper extension, we’ll use Manifest V3, the latest version of Chrome’s extension platform.

{
  "manifest_version": 3,
  "name": "Smart Tab Grouper",
  "version": "1.0.0",
  "description": "Automatically organize your Chrome tabs with intelligent grouping rules",
  "permissions": [
    "tabs",
    "storage",
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "options_page": "options.html",
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

This manifest requests the necessary permissions to read tab information, store user preferences, and execute scripts. The tabs permission is essential for our auto group tabs functionality, while storage allows us to save grouping rules.


The popup is what users see when they click your extension’s icon in the toolbar. Let’s create a clean, intuitive interface for our tab grouper extension.

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Smart Tab Grouper</title>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header>
      <h1>Smart Tab Grouper</h1>
      <p class="subtitle">Organize your tabs intelligently</p>
    </header>

    <div class="stats">
      <div class="stat-item">
        <span class="stat-value" id="totalTabs">0</span>
        <span class="stat-label">Open Tabs</span>
      </div>
      <div class="stat-item">
        <span class="stat-value" id="totalGroups">0</span>
        <span class="stat-label">Groups</span>
      </div>
    </div>

    <div class="actions">
      <button id="autoGroupBtn" class="primary-btn">
        Auto Group All Tabs
      </button>
      <button id="createGroupBtn" class="secondary-btn">
        Create Custom Group
      </button>
      <button id="clearGroupsBtn" class="danger-btn">
        Clear All Groups
      </button>
    </div>

    <div class="groups-list" id="groupsList">
      <h3>Current Groups</h3>
      <div id="groupsContainer"></div>
    </div>
  </div>
  <script src="popup.js"></script>
</body>
</html>

popup.css

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

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  width: 350px;
  background: #f5f5f5;
}

.container {
  padding: 16px;
}

header {
  text-align: center;
  margin-bottom: 16px;
}

h1 {
  font-size: 18px;
  color: #333;
  margin-bottom: 4px;
}

.subtitle {
  font-size: 12px;
  color: #666;
}

.stats {
  display: flex;
  justify-content: space-around;
  background: white;
  padding: 12px;
  border-radius: 8px;
  margin-bottom: 16px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

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

.stat-value {
  display: block;
  font-size: 24px;
  font-weight: bold;
  color: #4a90d9;
}

.stat-label {
  font-size: 11px;
  color: #666;
  text-transform: uppercase;
}

.actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
}

button {
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.primary-btn {
  background: #4a90d9;
  color: white;
}

.primary-btn:hover {
  background: #3a7bc8;
}

.secondary-btn {
  background: white;
  color: #4a90d9;
  border: 1px solid #4a90d9;
}

.secondary-btn:hover {
  background: #f0f7ff;
}

.danger-btn {
  background: white;
  color: #d94a4a;
  border: 1px solid #d94a4a;
}

.danger-btn:hover {
  background: #fff5f5;
}

.groups-list {
  background: white;
  border-radius: 8px;
  padding: 12px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.groups-list h3 {
  font-size: 14px;
  color: #333;
  margin-bottom: 12px;
}

.group-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  border-radius: 4px;
  margin-bottom: 4px;
  background: #f9f9f9;
}

.group-color {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin-right: 8px;
}

.group-name {
  flex: 1;
  font-size: 13px;
  color: #333;
}

.group-count {
  font-size: 12px;
  color: #666;
}

The Logic: Implementing Auto Group Tabs

Now comes the core functionality. We’ll create a background script that handles the actual tab grouping logic. This is where our tab grouper extension really shines.

background.js

// Background script for Smart Tab Grouper Extension

// Color palette for groups
const GROUP_COLORS = [
  '#f87171', '#fb923c', '#fbbf24', '#a3e635', 
  '#4ade80', '#2dd4bf', '#38bdf8', '#818cf8', 
  '#c084fc', '#f472b6'
];

// Default grouping rules
let groupingRules = {
  byDomain: true,
  byPattern: false,
  customRules: []
};

// Initialize extension
chrome.runtime.onInstalled.addListener(() => {
  chrome.storage.local.get(['groupingRules'], (result) => {
    if (result.groupingRules) {
      groupingRules = result.groupingRules;
    } else {
      chrome.storage.local.set({ groupingRules });
    }
  });
});

// Get all tabs in current window
async function getAllTabs() {
  const tabs = await chrome.tabs.query({ currentWindow: true });
  return tabs;
}

// Extract domain from URL
function extractDomain(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.hostname.replace('www.', '');
  } catch {
    return 'unknown';
  }
}

// Group tabs by domain
async function groupByDomain() {
  const tabs = await getAllTabs();
  const domainGroups = {};

  // Categorize tabs by domain
  tabs.forEach(tab => {
    const domain = extractDomain(tab.url);
    if (!domainGroups[domain]) {
      domainGroups[domain] = [];
    }
    domainGroups[domain].push(tab);
  });

  // Create groups for each domain with multiple tabs
  let colorIndex = 0;
  const groups = [];

  for (const [domain, domainTabs] of Object.entries(domainGroups)) {
    if (domainTabs.length > 1) {
      const groupName = domain.length > 20 ? domain.substring(0, 20) + '...' : domain;
      
      const groupId = await chrome.tabs.group({
        tabIds: domainTabs.map(t => t.id)
      });
      
      await chrome.tabGroups.update(groupId, {
        title: groupName,
        color: GROUP_COLORS[colorIndex % GROUP_COLORS.length]
      });

      groups.push({ name: groupName, count: domainTabs.length });
      colorIndex++;
    }
  }

  return groups;
}

// Auto-group all tabs
async function autoGroupTabs() {
  try {
    // First, remove all existing groups
    const groups = await chrome.tabGroups.query({});
    for (const group of groups) {
      await chrome.tabGroups.ungroup(group.id);
    }

    // Then create new groups by domain
    const newGroups = await groupByDomain();
    
    return {
      success: true,
      groups: newGroups
    };
  } catch (error) {
    console.error('Error auto-grouping tabs:', error);
    return {
      success: false,
      error: error.message
    };
  }
}

// Clear all tab groups
async function clearAllGroups() {
  try {
    const groups = await chrome.tabGroups.query({});
    for (const group of groups) {
      await chrome.tabGroups.ungroup(group.id);
    }
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

// Get current groups info
async function getGroupsInfo() {
  const tabs = await getAllTabs();
  const groups = await chrome.tabGroups.query({});
  
  return {
    totalTabs: tabs.length,
    totalGroups: groups.length,
    groups: groups.map(g => ({
      id: g.id,
      title: g.title,
      color: g.color,
      tabCount: tabs.filter(t => t.groupId === g.id).length
    }))
  };
}

// Message handler for popup communication
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  switch (message.action) {
    case 'autoGroup':
      autoGroupTabs().then(result => sendResponse(result));
      return true;
    case 'clearGroups':
      clearAllGroups().then(result => sendResponse(result));
      return true;
    case 'getGroupsInfo':
      getGroupsInfo().then(result => sendResponse(result));
      return true;
  }
});

This background script is the brain of our tab grouper extension. It handles:

  • Extracting domains from URLs
  • Grouping tabs by domain
  • Creating color-coded groups
  • Managing existing groups

Now let’s connect our popup HTML to the background script with JavaScript:

popup.js

document.addEventListener('DOMContentLoaded', async () => {
  // Load initial stats
  await updateStats();

  // Set up button listeners
  document.getElementById('autoGroupBtn').addEventListener('click', async () => {
    const btn = document.getElementById('autoGroupBtn');
    btn.disabled = true;
    btn.textContent = 'Grouping...';

    try {
      const result = await chrome.runtime.sendMessage({ action: 'autoGroup' });
      if (result.success) {
        await updateStats();
        alert(`Successfully created ${result.groups.length} groups!`);
      } else {
        alert('Error grouping tabs: ' + result.error);
      }
    } catch (error) {
      alert('Error: ' + error.message);
    }

    btn.disabled = false;
    btn.textContent = 'Auto Group All Tabs';
  });

  document.getElementById('clearGroupsBtn').addEventListener('click', async () => {
    try {
      await chrome.runtime.sendMessage({ action: 'clearGroups' });
      await updateStats();
      alert('All groups cleared!');
    } catch (error) {
      alert('Error: ' + error.message);
    }
  });

  document.getElementById('createGroupBtn').addEventListener('click', async () => {
    const groupName = prompt('Enter group name:');
    if (groupName) {
      alert('Custom grouping feature coming soon!');
    }
  });
});

async function updateStats() {
  try {
    const info = await chrome.runtime.sendMessage({ action: 'getGroupsInfo' });
    
    document.getElementById('totalTabs').textContent = info.totalTabs;
    document.getElementById('totalGroups').textContent = info.totalGroups;

    const container = document.getElementById('groupsContainer');
    container.innerHTML = '';

    if (info.groups.length === 0) {
      container.innerHTML = '<p class="empty-message">No groups yet</p>';
    } else {
      info.groups.forEach(group => {
        const div = document.createElement('div');
        div.className = 'group-item';
        div.innerHTML = `
          <div style="display: flex; align-items: center;">
            <span class="group-color" style="background: ${group.color}"></span>
            <span class="group-name">${group.title}</span>
          </div>
          <span class="group-count">${group.tabCount} tabs</span>
        `;
        container.appendChild(div);
      });
    }
  } catch (error) {
    console.error('Error updating stats:', error);
  }
}

Adding Content Script for Advanced Features

To make our tab grouper extension even smarter, let’s add a content script that can analyze page content and suggest appropriate groups. This is particularly useful for organizing tabs Chrome based on content type.

content.js

// Content script for analyzing page content

// Detect page type based on URL and content
function detectPageType() {
  const url = window.location.href;
  const title = document.title.toLowerCase();

  // Social media
  if (url.includes('facebook.com') || url.includes('twitter.com') || 
      url.includes('linkedin.com') || url.includes('instagram.com')) {
    return 'social';
  }

  // Email
  if (url.includes('gmail.com') || url.includes('outlook.com') || 
      url.includes('mail.google.com')) {
    return 'email';
  }

  // Development
  if (url.includes('github.com') || url.includes('stackoverflow.com') || 
      url.includes('dev.to') || url.includes('gitlab.com')) {
    return 'development';
  }

  // Video
  if (url.includes('youtube.com') || url.includes('vimeo.com') || 
      url.includes('twitch.tv')) {
    return 'video';
  }

  // News
  if (url.includes('news') || url.includes('medium.com') || 
      url.includes('reddit.com')) {
    return 'news';
  }

  // Shopping
  if (url.includes('amazon.com') || url.includes('ebay.com') || 
      url.includes('etsy.com')) {
    return 'shopping';
  }

  return 'general';
}

// Listen for messages from popup or background
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'analyzePage') {
    const pageType = detectPageType();
    const domain = window.location.hostname;
    
    sendResponse({
      pageType,
      domain,
      title: document.title,
      url: window.location.href
    });
  }
  
  return true;
});

Creating an Options Page

For a complete tab grouper extension, users should be able to customize their grouping behavior. Let’s create an options page:

options.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Smart Tab Grouper - Options</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
      background: #f5f5f5;
    }
    h1 {
      color: #333;
      margin-bottom: 20px;
    }
    .option-group {
      background: white;
      padding: 16px;
      border-radius: 8px;
      margin-bottom: 16px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    label {
      display: block;
      margin-bottom: 8px;
      font-weight: 500;
    }
    input[type="checkbox"] {
      margin-right: 8px;
    }
    .save-btn {
      background: #4a90d9;
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-size: 14px;
    }
    .save-btn:hover {
      background: #3a7bc8;
    }
  </style>
</head>
<body>
  <h1>Smart Tab Grouper - Options</h1>
  
  <div class="option-group">
    <label>
      <input type="checkbox" id="groupByDomain" checked>
      Auto group tabs by domain
    </label>
    <label>
      <input type="checkbox" id="groupByPattern">
      Group by custom URL patterns
    </label>
    <label>
      <input type="checkbox" id="detectDuplicates" checked>
      Detect and highlight duplicate tabs
    </label>
    <label>
      <input type="checkbox" id="suspendInactive" checked>
      Suspend inactive tabs after 30 minutes
    </label>
  </div>

  <button class="save-btn" id="saveBtn">Save Options</button>

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

options.js

document.addEventListener('DOMContentLoaded', () => {
  // Load saved options
  chrome.storage.local.get(['groupingRules'], (result) => {
    const rules = result.groupingRules || {};
    document.getElementById('groupByDomain').checked = rules.byDomain !== false;
    document.getElementById('groupByPattern').checked = rules.byPattern || false;
    document.getElementById('detectDuplicates').checked = rules.detectDuplicates !== false;
    document.getElementById('suspendInactive').checked = rules.suspendInactive !== false;
  });

  // Save options
  document.getElementById('saveBtn').addEventListener('click', () => {
    const rules = {
      byDomain: document.getElementById('groupByDomain').checked,
      byPattern: document.getElementById('groupByPattern').checked,
      detectDuplicates: document.getElementById('detectDuplicates').checked,
      suspendInactive: document.getElementById('suspendInactive').checked
    };

    chrome.storage.local.set({ groupingRules: rules }, () => {
      alert('Options saved!');
    });
  });
});

Testing Your Extension

Now that we’ve built all the components, let’s test our tab grouper 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
  5. Open multiple tabs across different domains
  6. Click the extension icon and try Auto Group All Tabs

You should see your tabs automatically organized by domain with color-coded groups!


Advanced Features to Consider

Once you have the basic auto group tabs functionality working, consider adding these advanced features:

1. Keyboard Shortcuts

Implement Chrome’s commands API to allow users to trigger grouping with keyboard shortcuts.

2. Sync Across Devices

Use chrome.storage.sync to save grouping rules and preferences across all devices where the user is signed in.

3. Custom Grouping Rules

Allow users to define custom rules based on URL patterns, page titles, or content keywords.

Implement a searchable list of all open tabs for quick navigation.

5. Session Management

Save and restore tab groups across browser sessions.


Conclusion

Building a tab grouper extension is an excellent project that combines practical utility with meaningful Chrome extension development skills. You’ve learned how to:

  • Set up a Manifest V3 Chrome extension
  • Create an intuitive popup interface
  • Implement auto group tabs functionality using the TabGroups API
  • Build an options page for user customization
  • Test and debug your extension

This project provides a solid foundation for building more complex Chrome extensions. The skills you developed here—working with Chrome APIs, managing browser tabs, and creating intuitive user interfaces—transfer directly to other extension projects.

Remember, the best extensions solve real problems. As you continue development, gather feedback from users and iterate on features. A tab grouper extension that truly helps people organize tabs Chrome can become a valuable tool for thousands of users.

Now it’s your turn to take this foundation and build something amazing!

No previous article
No next article