Build a Duplicate Tab Finder Chrome Extension: Clean Up Your Browser

20 min read

Build a Duplicate Tab Finder Chrome Extension: Clean Up Your Browser

Build a Duplicate Tab Finder Chrome Extension: Clean Up Your Browser

If you are like most Chrome users, you have probably experienced the frustration of having dozens of duplicate tabs open without realizing it. Whether it is the same article opened multiple times, research pages that got duplicated during your workflow, or simply forgetting that you already had a tab open, duplicate tabs accumulate quickly and create chaos in your browsing experience. In this comprehensive guide, we will walk you through building your own Duplicate Tab Finder Chrome Extension from scratch, giving you the power to automatically detect, manage, and remove duplicate tabs with just a few clicks.

The Chrome browser has become the backbone of modern productivity, serving as our gateway to information, communication, and work. However, with great power comes great clutter, and the humble tab system that makes Chrome so versatile can quickly become overwhelming. Building a custom duplicate tab finder extension is not just about cleaning up your browser—it is about understanding how Chrome extensions work, learning the Chrome APIs, and creating a tool tailored specifically to your needs.

Throughout this tutorial, we will cover everything from setting up your development environment to implementing the core duplicate detection logic, from designing an intuitive user interface to packaging your extension for distribution. By the end, you will have a fully functional duplicate tab finder extension that you can use daily and share with others.


Understanding the Problem: Why Duplicate Tabs Matter

Before we dive into the code, it is essential to understand why duplicate tabs are more than just a minor inconvenience. When you have multiple copies of the same webpage open, you are consuming system resources unnecessarily. Each tab in Chrome runs in its own process, which means that identical pages are duplicating memory usage, CPU cycles, and network bandwidth. If you have ten tabs pointing to the same URL, you are essentially wasting nine times the resources needed to display that content once.

Beyond resource consumption, duplicate tabs create cognitive overhead. When you are trying to find specific information or return to a page you were reading, duplicate tabs make it harder to locate what you need. You might waste time clicking through multiple copies of the same page, or worse, you might accidentally close the wrong tab and lose important content. For researchers, developers, and anyone who relies heavily on browser-based workflows, duplicate tabs can significantly impact productivity.

The challenge is that manually tracking which tabs are duplicates is nearly impossible when you have dozens of open tabs. This is where a dedicated Chrome extension shines—by automatically scanning your tabs, identifying duplicates based on multiple criteria, and presenting you with an easy way to clean them up.


Prerequisites and Development Environment Setup

Before we begin building our extension, let us ensure you have everything needed for Chrome extension development. The good news is that Chrome extensions can be developed with nothing more than a text editor and Chrome itself. You do not need complex frameworks or expensive tools—a simple code editor like Visual Studio Code or even a basic text editor will suffice.

You will need to have Google Chrome installed on your computer, along with a modern code editor. We recommend Visual Studio Code because it has excellent extensions for JavaScript development and provides helpful features like syntax highlighting and auto-completion. You should also be familiar with basic HTML, CSS, and JavaScript, as these are the technologies that power Chrome extensions.

To create your project, start by creating a new folder on your computer named “duplicate-tab-finder”. Inside this folder, you will create several files that together form your Chrome extension. The most important of these is the manifest.json file, which tells Chrome about your extension and what it can do.


Creating the Manifest File

Every Chrome extension requires a manifest.json file that defines the extension’s configuration, permissions, and capabilities. For our duplicate tab finder, we need to specify that our extension requires access to the tabs API to read tab information and the scripting API to interact with web pages if needed.

Create a file named manifest.json in your project folder and add the following content:

{
  "manifest_version": 3,
  "name": "Duplicate Tab Finder",
  "version": "1.0",
  "description": "Find and remove duplicate tabs to clean up your browser",
  "permissions": [
    "tabs"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icon16.png",
      "48": "icon48.png",
      "128": "icon128.png"
    }
  },
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  }
}

This manifest uses Manifest V3, which is the current standard for Chrome extensions. The permissions array includes “tabs”, which allows our extension to access information about all open tabs in the browser. The action section defines what happens when you click the extension icon—we will create a popup interface that displays duplicate tabs and provides options to manage them.


Building the Popup Interface

The popup is what users see when they click on your extension icon in the Chrome toolbar. For our duplicate tab finder, we need a clean interface that shows users which tabs are duplicates and allows them to select which ones to close.

Create a file named popup.html in your project folder. This will contain the HTML structure for our popup interface:

<!DOCTYPE html>
<html>
<head>
  <style>
    body {
      width: 400px;
      padding: 20px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background-color: #f5f5f5;
    }
    h1 {
      font-size: 18px;
      margin-bottom: 15px;
      color: #333;
    }
    .stats {
      background: white;
      padding: 15px;
      border-radius: 8px;
      margin-bottom: 15px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .stat-item {
      display: flex;
      justify-content: space-between;
      margin-bottom: 8px;
    }
    .stat-value {
      font-weight: bold;
      color: #4285f4;
    }
    .duplicate-group {
      background: white;
      border-radius: 8px;
      margin-bottom: 10px;
      overflow: hidden;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .duplicate-header {
      background: #e8f0fe;
      padding: 10px 15px;
      font-weight: bold;
      color: #1a73e8;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .duplicate-count {
      font-size: 12px;
      background: #1a73e8;
      color: white;
      padding: 2px 8px;
      border-radius: 10px;
    }
    .tab-item {
      padding: 10px 15px;
      border-top: 1px solid #eee;
      display: flex;
      align-items: center;
      gap: 10px;
    }
    .tab-item:hover {
      background: #f9f9f9;
    }
    .tab-favicon {
      width: 16px;
      height: 16px;
    }
    .tab-title {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-size: 13px;
    }
    .tab-url {
      flex: 1;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      font-size: 11px;
      color: #666;
    }
    .close-btn {
      background: none;
      border: none;
      cursor: pointer;
      padding: 5px;
      border-radius: 4px;
      color: #666;
    }
    .close-btn:hover {
      background: #fee;
      color: #d32f2f;
    }
    .actions {
      margin-top: 15px;
      display: flex;
      gap: 10px;
    }
    button {
      flex: 1;
      padding: 10px;
      border: none;
      border-radius: 6px;
      cursor: pointer;
      font-weight: bold;
      transition: background 0.2s;
    }
    .btn-primary {
      background: #1a73e8;
      color: white;
    }
    .btn-primary:hover {
      background: #1557b0;
    }
    .btn-secondary {
      background: white;
      color: #333;
      border: 1px solid #ddd;
    }
    .btn-secondary:hover {
      background: #f5f5f5;
    }
    .no-duplicates {
      text-align: center;
      padding: 30px;
      color: #666;
    }
    .loading {
      text-align: center;
      padding: 30px;
      color: #666;
    }
  </style>
</head>
<body>
  <h1>🔍 Duplicate Tab Finder</h1>
  <div class="stats">
    <div class="stat-item">
      <span>Total Tabs:</span>
      <span class="stat-value" id="totalTabs">-</span>
    </div>
    <div class="stat-item">
      <span>Duplicate Groups:</span>
      <span class="stat-value" id="duplicateGroups">-</span>
    </div>
    <div class="stat-item">
      <span>Duplicate Tabs:</span>
      <span class="stat-value" id="duplicateCount">-</span>
    </div>
  </div>
  <div id="duplicatesList"></div>
  <div class="actions">
    <button class="btn-secondary" id="refreshBtn">Refresh</button>
    <button class="btn-primary" id="closeAllBtn">Close Duplicates</button>
  </div>
  <script src="popup.js"></script>
</body>
</html>

This HTML file creates a clean, modern popup interface with statistics about your tabs and a list of duplicate groups. Each duplicate group shows all tabs that share the same URL, and users can close individual tabs or all duplicates at once.


Implementing the Core Logic in JavaScript

Now comes the most important part—the JavaScript that powers our extension. Create a file named popup.js that will handle scanning for duplicates, displaying them to the user, and closing duplicate tabs.

document.addEventListener('DOMContentLoaded', initialize);

function initialize() {
  loadDuplicates();
  
  document.getElementById('refreshBtn').addEventListener('click', loadDuplicates);
  document.getElementById('closeAllBtn').addEventListener('click', closeAllDuplicates);
}

function loadDuplicates() {
  const duplicatesList = document.getElementById('duplicatesList');
  duplicatesList.innerHTML = '<div class="loading">Scanning for duplicate tabs...</div>';
  
  chrome.tabs.query({}, function(tabs) {
    const duplicateGroups = findDuplicateTabs(tabs);
    displayDuplicates(duplicateGroups, tabs.length);
  });
}

function findDuplicateTabs(tabs) {
  const urlMap = new Map();
  
  tabs.forEach((tab, index) => {
    if (!tab.url || tab.url.startsWith('chrome://') || tab.url.startsWith('chrome-extension://')) {
      return;
    }
    
    const normalizedUrl = normalizeUrl(tab.url);
    
    if (!urlMap.has(normalizedUrl)) {
      urlMap.set(normalizedUrl, []);
    }
    
    urlMap.get(normalizedUrl).push({ ...tab, index });
  });
  
  const duplicates = [];
  urlMap.forEach((tabGroup, url) => {
    if (tabGroup.length > 1) {
      duplicates.push({
        url: url,
        tabs: tabGroup
      });
    }
  });
  
  return duplicates;
}

function normalizeUrl(url) {
  try {
    const urlObj = new URL(url);
    
    let normalized = urlObj.hostname + urlObj.pathname;
    
    if (normalized.endsWith('/')) {
      normalized = normalized.slice(0, -1);
    }
    
    normalized = normalized.toLowerCase();
    
    return normalized;
  } catch (e) {
    return url;
  }
}

function displayDuplicates(duplicateGroups, totalTabs) {
  document.getElementById('totalTabs').textContent = totalTabs;
  document.getElementById('duplicateGroups').textContent = duplicateGroups.length;
  
  let duplicateCount = 0;
  duplicateGroups.forEach(group => {
    duplicateCount += group.tabs.length - 1;
  });
  document.getElementById('duplicateCount').textContent = duplicateCount;
  
  const duplicatesList = document.getElementById('duplicatesList');
  
  if (duplicateGroups.length === 0) {
    duplicatesList.innerHTML = '<div class="no-duplicates">✅ No duplicate tabs found!<br>Your browser is clean.</div>';
    document.getElementById('closeAllBtn').disabled = true;
    return;
  }
  
  document.getElementById('closeAllBtn').disabled = false;
  
  let html = '';
  
  duplicateGroups.forEach((group, groupIndex) => {
    const primaryTab = group.tabs[0];
    const duplicateTabs = group.tabs.slice(1);
    
    html += `
      <div class="duplicate-group">
        <div class="duplicate-header">
          <span>Duplicate Group #${groupIndex + 1}</span>
          <span class="duplicate-count">${group.tabs.length} tabs</span>
        </div>
    `;
    
    html += `
      <div class="tab-item">
        <img class="tab-favicon" src="${primaryTab.favIconUrl || ''}" alt="">
        <div style="flex:1;overflow:hidden;">
          <div class="tab-title">${escapeHtml(primaryTab.title)}</div>
          <div class="tab-url">${escapeHtml(primaryTab.url)}</div>
        </div>
        <span style="color:#4caf50;font-size:11px;">KEEP</span>
      </div>
    `;
    
    duplicateTabs.forEach(tab => {
      html += `
        <div class="tab-item">
          <img class="tab-favicon" src="${tab.favIconUrl || ''}" alt="">
          <div style="flex:1;overflow:hidden;">
            <div class="tab-title">${escapeHtml(tab.title)}</div>
            <div class="tab-url">${escapeHtml(tab.url)}</div>
          </div>
          <button class="close-btn" data-tab-id="${tab.id}" title="Close this tab">✕</button>
        </div>
      `;
    });
    
    html += '</div>';
  });
  
  duplicatesList.innerHTML = html;
  
  document.querySelectorAll('.close-btn').forEach(btn => {
    btn.addEventListener('click', function() {
      const tabId = parseInt(this.getAttribute('data-tab-id'));
      chrome.tabs.remove(tabId, loadDuplicates);
    });
  });
}

function closeAllDuplicates() {
  chrome.tabs.query({}, function(tabs) {
    const duplicateGroups = findDuplicateTabs(tabs);
    const tabsToClose = [];
    
    duplicateGroups.forEach(group => {
      const duplicateTabs = group.tabs.slice(1);
      duplicateTabs.forEach(tab => {
        tabsToClose.push(tab.id);
      });
    });
    
    if (tabsToClose.length > 0) {
      chrome.tabs.remove(tabsToClose, function() {
        loadDuplicates();
      });
    }
  });
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

This JavaScript code handles several critical functions. The findDuplicateTabs function iterates through all open tabs and groups them by their normalized URL. The normalization process ensures that tabs with slightly different URLs (such as those with trailing slashes or different protocols) are still recognized as duplicates. The displayDuplicates function renders this information in a user-friendly way, showing which tabs are duplicates and providing buttons to close them individually or all at once.


Adding Basic Icons

While you would typically create professional icons for a production extension, we can create simple placeholder icons for testing. Create PNG files named icon16.png, icon48.png, and icon128.png in your project folder. You can use any image editor to create simple colored squares, or you can download placeholder icons from various online sources.

For development purposes, Chrome will still load your extension even without icons, but they will appear with a default puzzle piece icon. When you are ready to publish your extension, you should create properly designed icons that represent your extension’s functionality.


Loading Your Extension in Chrome

Now that you have created all the necessary files, it is time to load your extension in Chrome and test it. Open Chrome and navigate to chrome://extensions/. In the top right corner, toggle the “Developer mode” switch to enable it. You should see new options appear, including a “Load unpacked” button.

Click “Load unpacked” and select the folder where you created your extension files. Chrome will load your extension and display it in the extensions list. You should now see your Duplicate Tab Finder extension icon in the Chrome toolbar.

Click the extension icon to open the popup. The extension will automatically scan all your open tabs, identify duplicates, and display them in the interface. You can click on individual close buttons to remove specific duplicate tabs, or click “Close Duplicates” to remove all duplicates at once while keeping one copy of each.


Enhancing Your Extension

The basic version we have built is fully functional, but there are many ways you could enhance it to make it even more useful. One valuable addition would be to implement detection of similar but not identical pages, such as different pages from the same domain. You could also add options to automatically close duplicates when they are detected, or to notify users when new duplicates are created.

Another useful enhancement would be to add keyboard shortcuts for quickly closing duplicates. You could also implement a feature that highlights tabs that have been open for a long time without being used, helping users identify tabs that might no longer be needed.

For a more advanced version, you could add synchronization capabilities that allow users to save their tab groups across different devices using Chrome’s sync storage API. This would make the extension even more powerful for users who work across multiple computers.


Understanding Chrome’s Tab API

To build truly powerful Chrome extensions, it is important to understand the full capabilities of Chrome’s tab APIs. The chrome.tabs API provides extensive functionality for querying, creating, updating, and manipulating tabs. You can access tab properties like the URL, title, favicon, window ID, and various states like whether the tab is active or pinned.

The chrome.windows API complements the tabs API by providing window-level functionality. Together, these APIs allow you to build sophisticated tab management tools that can organize tabs into groups, move them between windows, and perform bulk operations on multiple tabs at once.

Chrome also provides the chrome.tabGroups API, which allows you to work with tab groups introduced in recent Chrome versions. This could be useful for organizing your duplicates into groups for easier management.


Best Practices for Extension Development

When developing Chrome extensions, following best practices ensures your extension is reliable, secure, and provides a good user experience. Always request only the permissions your extension truly needs—requesting excessive permissions can make users suspicious and may cause issues with the Chrome Web Store review process.

Performance is also crucial. Extensions that consume excessive memory or CPU can negatively impact the browsing experience. The duplicate tab finder we built is designed to be lightweight, scanning tabs only when the popup is opened rather than running continuously in the background.

Finally, always test your extension thoroughly before publishing. Try different edge cases, such as having many tabs open, having tabs with unusual URLs, and having multiple windows open. Make sure your extension handles errors gracefully and provides useful feedback to users when something goes wrong.


Publishing Your Extension

Once you are satisfied with your extension, you can publish it to the Chrome Web Store to share it with other users. To publish, you will need to create a developer account and pay a one-time registration fee. Then, you can upload your extension’s source code, provide store listings with descriptions and screenshots, and publish it for all Chrome users to discover.

Before publishing, make sure to thoroughly test your extension and review Chrome’s policies to ensure your extension complies with all guidelines. Extensions that violate policies may be removed or rejected from the store.


Conclusion

Building a Duplicate Tab Finder Chrome Extension is an excellent project for learning Chrome extension development while creating something genuinely useful. The extension we built today can help you keep your browser organized, reduce memory usage, and improve your overall browsing productivity.

The skills you have learned in this tutorial—working with manifest files, creating popup interfaces, using Chrome’s tab APIs, and implementing core functionality in JavaScript—form the foundation for building many other types of Chrome extensions. With these tools, you can create extensions for bookmark management, tab grouping, productivity tracking, and much more.

Remember that the best extensions often come from solving personal pain points. As you use your Duplicate Tab Finder extension, think about other browser frustrations you experience that could be addressed with a custom extension. The possibilities are virtually unlimited, and now you have the knowledge to bring your ideas to life.

No previous article
No next article