Chrome Extension Tab Manager — Developer Guide

5 min read

Build a Tab Manager Extension — Full Tutorial

What We’re Building

Prerequisites

Step 1: manifest.json

{
  "manifest_version": 3,
  "name": "Tab Manager",
  "version": "1.0.0",
  "permissions": ["tabs", "tabGroups", "storage"],
  "action": { "default_popup": "popup.html", "default_icon": "icon.png" },
  "background": { "service_worker": "background.js" }
}

Step 2: Popup HTML/CSS

Step 3: Popup JavaScript

import { createMessenger } from '@theluckystrike/webext-messaging';
import { createStorage, defineSchema } from '@theluckystrike/webext-storage';

type Messages = {
  getTabs: { request: void; response: [chrome.tabs](https://theluckystrike.github.io/extension-monetization-playbook/monetization/api-monetization).Tab[] };
  closeTab: { request: { tabId: number }; response: void };
  pinTab: { request: { tabId: number; pinned: boolean }; response: void };
  groupTabs: { request: { tabIds: number[]; title: string }; response: void };
};

const messenger = createMessenger<Messages>();
const storage = createStorage(defineSchema({ lastSearch: 'string' }), 'local');

// Load tabs
const tabs = await messenger.sendMessage('getTabs', undefined);
renderTabList(tabs);

// Search filter
const searchInput = document.getElementById('search');
searchInput.value = await storage.get('lastSearch') || '';
searchInput.addEventListener('input', async (e) => {
  const query = e.target.value.toLowerCase();
  await storage.set('lastSearch', query);
  filterTabs(query);
});

Step 4: Background Service Worker

const messenger = createMessenger<Messages>();

messenger.onMessage('getTabs', async () => {
  return await [chrome.tabs](https://theluckystrike.github.io/extension-monetization-playbook/monetization/api-monetization).query({});
});

messenger.onMessage('closeTab', async ({ tabId }) => {
  await [chrome.tabs](https://theluckystrike.github.io/extension-monetization-playbook/monetization/api-monetization).remove(tabId);
});

messenger.onMessage('pinTab', async ({ tabId, pinned }) => {
  await [chrome.tabs](https://theluckystrike.github.io/extension-monetization-playbook/monetization/api-monetization).update(tabId, { pinned });
});

messenger.onMessage('groupTabs', async ({ tabIds, title }) => {
  const groupId = await [chrome.tabs](https://theluckystrike.github.io/extension-monetization-playbook/monetization/api-monetization).group({ tabIds });
  await chrome.tabGroups.update(groupId, { title, color: 'blue' });
});

Step 5: Features

Step 6: Duplicate Detection

function findDuplicates(tabs) {
  const urlMap = new Map();
  tabs.forEach(tab => {
    const existing = urlMap.get(tab.url) || [];
    existing.push(tab);
    urlMap.set(tab.url, existing);
  });
  return [...urlMap.entries()].filter(([, tabs]) => tabs.length > 1);
}

Step 7: Polish

Testing

What You Learned


Turn Your Extension Into a Business

Ready to monetize? The Extension Monetization Playbook covers freemium models, Stripe integration, subscription architecture, and growth strategies for Chrome extension developers. —

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.

No previous article
No next article