Build an AI-Powered Tab Organizer Extension
Browser tab clutter is one of the most common productivity bottlenecks for Chrome users. With the average professional having 10-30 tabs open at any given time, finding the right tab when you need it becomes a daily frustration. The solution? An AI-powered tab organizer that automatically groups related tabs, suggests organization strategies, and learns from your browsing patterns.
In this comprehensive guide, we will walk you through building a complete AI-powered tab organizer extension for Chrome using Manifest V3. By the end of this tutorial, you will have a fully functional extension that can analyze open tabs, categorize them using AI, and organize them into logical groups with a single click.
Why Build an AI Tab Organizer?
The Chrome Web Store has numerous tab management extensions, but few leverage artificial intelligence to truly understand user intent. Here’s why building an AI-powered solution is worth your time:
The Problem with Traditional Tab Management
Most existing tab organizers rely on rigid rules or manual grouping. Users must manually create groups, drag and drop tabs, or define patterns for categorization. This approach is time-consuming and doesn’t adapt to individual browsing habits. An AI-powered solution can analyze page content, understand context, and automatically suggest or create groups based on what you actually do.
The Power of AI for Tab Organization
Modern AI models can understand text content, extract meaningful keywords, and categorize pages with remarkable accuracy. By integrating AI into your tab organizer, you can:
- Automatically detect and group tabs by topic (research, shopping, social media, work)
- Suggest relevant tabs based on your current task
- Learn your browsing patterns over time
- Provide smart recommendations for managing tab overload
- Reduce memory usage by identifying and suspending inactive tab groups
Project Architecture Overview
Before diving into code, let’s understand the architecture of our AI-powered tab organizer extension.
Core Components
Our extension will consist of several key components working together:
- Popup Interface: A user-friendly popup that displays tab groups and allows quick actions
- Background Service Worker: Handles long-running tasks, API calls, and state management
- Content Scripts: Extract page content and metadata for AI analysis
- AI Integration Module: Connects with external AI APIs for intelligent categorization
- Storage System: Persists user preferences and learned patterns
Technology Stack
- Manifest V3: The latest Chrome extension platform
- Vanilla JavaScript: No framework dependencies for maximum compatibility
- Chrome APIs: tabs, tabGroups, storage, and runtime APIs
- External AI Service: Integration with NLP APIs for content analysis
Step 1: Setting Up the Manifest
Every Chrome extension starts with the manifest file. Let’s create a Manifest V3 configuration that includes all the permissions we need.
{
"manifest_version": 3,
"name": "AI Tab Organizer",
"version": "1.0.0",
"description": "Automatically organize your tabs using AI-powered smart grouping",
"permissions": [
"tabs",
"tabGroups",
"storage",
"activeTab"
],
"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"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_idle"
}
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
This manifest grants our extension access to the tabs API for reading tab information, the tabGroups API for creating visual groups, and storage for persisting user preferences. The background service worker will handle the heavy lifting of AI analysis.
Step 2: Creating the Popup Interface
The popup is the primary user interface for our extension. It needs to show current tab groups, provide organization controls, and display AI suggestions. Let’s create a clean, functional popup.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Tab Organizer</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
width: 380px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f8f9fa;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 16px;
text-align: center;
}
.header h1 { font-size: 18px; margin-bottom: 4px; }
.header p { font-size: 12px; opacity: 0.9; }
.content { padding: 16px; }
.stats {
display: flex;
justify-content: space-around;
background: white;
border-radius: 8px;
padding: 12px;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stat { text-align: center; }
.stat-value { font-size: 24px; font-weight: bold; color: #667eea; }
.stat-label { font-size: 11px; color: #666; }
.actions { display: flex; flex-direction: column; gap: 8px; }
button {
padding: 12px 16px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); }
.btn-secondary {
background: white;
color: #333;
border: 1px solid #ddd;
}
.btn-secondary:hover { background: #f5f5f5; }
.groups-list {
margin-top: 16px;
max-height: 300px;
overflow-y: auto;
}
.group-item {
background: white;
border-radius: 8px;
padding: 12px;
margin-bottom: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.group-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.group-name { font-weight: 600; color: #333; }
.group-count { font-size: 12px; color: #888; background: #f0f0f0; padding: 2px 8px; border-radius: 12px; }
.group-tabs { font-size: 12px; color: #666; }
.loading {
text-align: center;
padding: 20px;
color: #666;
}
.spinner {
width: 24px;
height: 24px;
border: 3px solid #f3f3f3;
border-top: 3px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 8px;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body>
<div class="header">
<h1>AI Tab Organizer</h1>
<p>Smart tab grouping powered by AI</p>
</div>
<div class="content">
<div class="stats">
<div class="stat">
<div class="stat-value" id="tabCount">0</div>
<div class="stat-label">Open Tabs</div>
</div>
<div class="stat">
<div class="stat-value" id="groupCount">0</div>
<div class="stat-label">Groups</div>
</div>
<div class="stat">
<div class="stat-value" id="savedMemory">0MB</div>
<div class="stat-label">Memory Saved</div>
</div>
</div>
<div class="actions">
<button class="btn-primary" id="organizeBtn">🧠 Auto-Organize Tabs</button>
<button class="btn-secondary" id="createGroupBtn">+ Create New Group</button>
<button class="btn-secondary" id="clearGroupsBtn">Clear All Groups</button>
</div>
<div class="groups-list" id="groupsList">
<div class="loading">Click "Auto-Organize" to analyze and group your tabs</div>
</div>
</div>
<script src="popup.js"></script>
</body>
</html>
The popup displays key statistics and provides three main actions: auto-organize tabs, create new groups manually, and clear all groups. The interface is clean and intuitive, making it easy for users to manage their tab chaos.
Step 3: Implementing the Background Service Worker
The background service worker is the brain of our extension. It handles communication between components, manages the AI integration, and coordinates tab operations. This is where the magic happens.
// background.js - Main service worker for AI Tab Organizer
// State management
let tabData = {};
let groups = [];
let aiAnalysisInProgress = false;
// Initialize extension
chrome.runtime.onInstalled.addListener(() => {
console.log('AI Tab Organizer installed');
initializeStorage();
});
function initializeStorage() {
chrome.storage.local.get(['tabGroups', 'settings'], (result) => {
if (!result.tabGroups) {
chrome.storage.local.set({ tabGroups: [], settings: {} });
}
});
}
// Listen for messages from popup and content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.action) {
case 'getTabData':
handleGetTabData(sendResponse);
return true;
case 'organizeTabs':
handleOrganizeTabs(sendResponse);
return true;
case 'createGroup':
handleCreateGroup(message.data, sendResponse);
return true;
case 'clearGroups':
handleClearGroups(sendResponse);
return true;
case 'analyzeTab':
handleAnalyzeTab(message.tabId, sendResponse);
return true;
}
});
// Get all open tabs and their metadata
async function handleGetTabData(sendResponse) {
try {
const tabs = await chrome.tabs.query({ currentWindow: true });
const tabInfo = await Promise.all(tabs.map(async (tab) => {
return {
id: tab.id,
title: tab.title,
url: tab.url,
favicon: tab.favIconUrl,
active: tab.active,
pinned: tab.pinned,
groupId: tab.groupId
};
}));
sendResponse({ success: true, tabs: tabInfo });
} catch (error) {
console.error('Error getting tab data:', error);
sendResponse({ success: false, error: error.message });
}
}
// Main AI organization logic
async function handleOrganizeTabs(sendResponse) {
if (aiAnalysisInProgress) {
sendResponse({ success: false, error: 'Analysis already in progress' });
return;
}
try {
aiAnalysisInProgress = true;
// Get all tabs in current window
const tabs = await chrome.tabs.query({ currentWindow: true });
// Extract tab content for AI analysis
const tabContent = await extractTabContent(tabs);
// Use AI to categorize tabs
const categories = await analyzeTabsWithAI(tabContent);
// Create groups based on AI analysis
const createdGroups = await createTabGroups(tabs, categories);
// Store group information
chrome.storage.local.set({ tabGroups: createdGroups });
aiAnalysisInProgress = false;
sendResponse({ success: true, groups: createdGroups, categoryCount: categories.length });
} catch (error) {
console.error('Error organizing tabs:', error);
aiAnalysisInProgress = false;
sendResponse({ success: false, error: error.message });
}
}
// Extract content from tabs for AI analysis
async function extractTabContent(tabs) {
const contentPromises = tabs.map(async (tab) => {
try {
// Try to get page content through scripting
const results = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => {
// Get meta description and title
const metaDesc = document.querySelector('meta[name="description"]')?.content || '';
const ogTitle = document.querySelector('meta[property="og:title"]')?.content || '';
const h1 = document.querySelector('h1')?.innerText || '';
// Get body text (first 500 characters)
const body = document.body?.innerText?.substring(0, 500) || '';
return {
title: document.title,
url: window.location.href,
metaDescription: metaDesc,
ogTitle: ogTitle,
h1: h1,
bodyText: body
};
}
});
return {
tabId: tab.id,
url: tab.url,
title: tab.title,
content: results[0]?.result || null
};
} catch (error) {
// For tabs that can't be accessed (e.g., chrome:// URLs)
return {
tabId: tab.id,
url: tab.url,
title: tab.title,
content: null
};
}
});
return Promise.all(contentPromises);
}
// AI-powered tab analysis using categorization
async function analyzeTabsWithAI(tabContent) {
// Define category patterns for AI to use
const categoryPatterns = {
'Work & Productivity': {
keywords: ['docs', 'drive', 'email', 'mail', 'slack', 'notion', 'asana', 'jira', 'trello', 'project', 'meeting', 'calendar', 'office', 'spreadsheet', 'presentation'],
domains: ['google.com/docs', 'drive.google', 'mail.google', 'outlook.com', 'office.com', 'notion.so', 'asana.com', 'slack.com']
},
'Development': {
keywords: ['github', 'gitlab', 'stackoverflow', 'dev', 'code', 'api', 'documentation', 'react', 'javascript', 'python', 'debug', 'terminal', 'localhost'],
domains: ['github.com', 'gitlab.com', 'stackoverflow.com', 'dev.to', 'medium.com/dev', 'localhost', '127.0.0.1']
},
'Research & Learning': {
keywords: ['research', 'wiki', 'article', 'blog', 'tutorial', 'guide', 'learn', 'course', 'study', 'documentation', 'mdn', 'w3c'],
domains: ['wikipedia.org', 'medium.com', 'dev.to', 'medium.com', 'coursera.org', 'udemy.com', 'udacity.com', 'khanacademy.org']
},
'Shopping': {
keywords: ['shop', 'buy', 'cart', 'order', 'price', 'product', 'store', 'amazon', 'ebay', 'checkout'],
domains: ['amazon.com', 'ebay.com', 'walmart.com', 'target.com', 'bestbuy.com', 'etsy.com']
},
'Social & Communication': {
keywords: ['social', 'facebook', 'twitter', 'instagram', 'linkedin', 'reddit', 'discord', 'messenger', 'whatsapp', 'telegram', 'chat'],
domains: ['facebook.com', 'twitter.com', 'x.com', 'instagram.com', 'linkedin.com', 'reddit.com', 'discord.com', 'messenger.com']
},
'Entertainment': {
keywords: ['video', 'movie', 'music', 'streaming', 'netflix', 'youtube', 'spotify', 'twitch', 'game', 'hulu', 'disney'],
domains: ['youtube.com', 'netflix.com', 'spotify.com', 'twitch.tv', 'hulu.com', 'disneyplus.com', 'primevideo.com']
},
'News & Information': {
keywords: ['news', 'headline', 'article', 'report', 'breaking', 'update', 'journal'],
domains: ['cnn.com', 'bbc.com', 'nytimes.com', 'theguardian.com', 'reuters.com', 'apnews.com']
},
'Finance': {
keywords: ['bank', 'finance', 'investment', 'stock', 'crypto', 'trading', 'portfolio', 'wallet'],
domains: ['bankofamerica.com', 'chase.com', 'wellsfargo.com', 'coinbase.com', 'binance.com', 'robinhood.com']
}
};
// Analyze each tab and assign categories
const categorizedTabs = tabContent.map(tab => {
const content = tab.content;
const titleLower = tab.title.toLowerCase();
const urlLower = tab.url.toLowerCase();
// Extract text to analyze
const textToAnalyze = content ?
`${tab.title} ${content.metaDescription} ${content.ogTitle} ${content.h1} ${content.bodyText}`.toLowerCase() :
`${tab.title} ${tab.url}`.toLowerCase();
// Find matching category
let bestCategory = 'General';
let highestScore = 0;
for (const [category, patterns] of Object.entries(categoryPatterns)) {
let score = 0;
// Check keywords
patterns.keywords.forEach(keyword => {
if (textToAnalyze.includes(keyword)) {
score += 2;
}
});
// Check domains
patterns.domains.forEach(domain => {
if (urlLower.includes(domain)) {
score += 5;
}
});
if (score > highestScore) {
highestScore = score;
bestCategory = category;
}
}
return {
tabId: tab.tabId,
title: tab.title,
url: tab.url,
category: highestScore > 0 ? bestCategory : 'General',
confidence: highestScore
};
});
// Group tabs by category
const grouped = {};
categorizedTabs.forEach(tab => {
if (!grouped[tab.category]) {
grouped[tab.category] = [];
}
grouped[tab.category].push(tab);
});
// Convert to array format
return Object.entries(grouped).map(([category, tabs]) => ({
category,
tabs,
tabIds: tabs.map(t => t.tabId)
}));
}
// Create Chrome tab groups based on AI analysis
async function createTabGroups(tabs, categories) {
const createdGroups = [];
// Remove existing groups first
const existingGroups = await chrome.tabGroups.query({ windowId: tabs[0].windowId });
for (const group of existingGroups) {
await chrome.tabGroups.remove(group.id);
}
// Define colors for different categories
const categoryColors = {
'Work & Productivity': 'grey',
'Development': 'blue',
'Research & Learning': 'green',
'Shopping': 'red',
'Social & Communication': 'cyan',
'Entertainment': 'purple',
'News & Information': 'yellow',
'Finance': 'orange',
'General': 'grey'
};
// Create groups for each category
for (const category of categories) {
if (category.tabIds.length < 1) continue;
try {
// Create the group
const groupId = await chrome.tabs.group({ tabIds: category.tabIds });
// Update group properties
await chrome.tabGroups.update(groupId, {
title: category.category,
color: categoryColors[category.category] || 'grey'
});
createdGroups.push({
id: groupId,
name: category.category,
color: categoryColors[category.category] || 'grey',
tabs: category.tabs.map(t => ({ id: t.tabId, title: t.title, url: t.url }))
});
} catch (error) {
console.error(`Error creating group for ${category.category}:`, error);
}
}
return createdGroups;
}
// Handle creating a new manual group
async function handleCreateGroup(data, sendResponse) {
try {
const { name, tabIds, color } = data;
const groupId = await chrome.tabs.group({ tabIds });
await chrome.tabGroups.update(groupId, { title: name, color: color || 'grey' });
sendResponse({ success: true, groupId });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
}
// Handle clearing all groups
async function handleClearGroups(sendResponse) {
try {
const tabs = await chrome.tabs.query({ currentWindow: true });
const windowId = tabs[0]?.windowId;
if (windowId) {
const groups = await chrome.tabGroups.query({ windowId });
for (const group of groups) {
await chrome.tabGroups.remove(group.id);
}
}
chrome.storage.local.set({ tabGroups: [] });
sendResponse({ success: true });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
}
// Analyze a single tab
async function handleAnalyzeTab(tabId, sendResponse) {
try {
const tab = await chrome.tabs.get(tabId);
const content = await extractTabContent([tab]);
const analysis = await analyzeTabsWithAI(content);
sendResponse({ success: true, analysis: analysis[0] });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
}
The background service worker handles all the complex logic: extracting content from web pages, analyzing that content using AI-powered categorization, and creating Chrome tab groups based on the analysis. It uses pattern matching against keywords and domains to intelligently categorize tabs without needing an external AI API.
Step 4: Creating the Popup JavaScript
Now let’s create the JavaScript that connects our popup UI to the background service worker.
// popup.js - Popup interface logic
document.addEventListener('DOMContentLoaded', async () => {
// Get DOM elements
const tabCountEl = document.getElementById('tabCount');
const groupCountEl = document.getElementById('groupCount');
const savedMemoryEl = document.getElementById('savedMemory');
const groupsListEl = document.getElementById('groupsList');
const organizeBtn = document.getElementById('organizeBtn');
const createGroupBtn = document.getElementById('createGroupBtn');
const clearGroupsBtn = document.getElementById('clearGroupsBtn');
// Load initial stats
await loadStats();
// Load existing groups
await loadGroups();
// Event listeners
organizeBtn.addEventListener('click', organizeTabs);
createGroupBtn.addEventListener('click', createNewGroup);
clearGroupsBtn.addEventListener('click', clearAllGroups);
async function loadStats() {
try {
const tabs = await chrome.tabs.query({ currentWindow: true });
const groups = await chrome.tabGroups.query({ currentWindow: true });
tabCountEl.textContent = tabs.length;
groupCountEl.textContent = groups.length;
// Estimate memory savings (rough calculation)
// Each suspended/background tab saves roughly 30-50MB
const memorySaved = Math.round((tabs.length - groups.length) * 35);
savedMemoryEl.textContent = `${memorySaved}MB`;
} catch (error) {
console.error('Error loading stats:', error);
}
}
async function loadGroups() {
try {
const result = await chrome.storage.local.get(['tabGroups']);
const groups = result.tabGroups || [];
if (groups.length === 0) {
groupsListEl.innerHTML = '<div class="loading">Click "Auto-Organize" to analyze and group your tabs</div>';
return;
}
groupsListEl.innerHTML = groups.map(group => `
<div class="group-item">
<div class="group-header">
<span class="group-name">${group.name}</span>
<span class="group-count">${group.tabs.length} tabs</span>
</div>
<div class="group-tabs">
${group.tabs.slice(0, 3).map(t => `• ${truncateTitle(t.title)}`).join('<br>')}
${group.tabs.length > 3 ? `<br>...and ${group.tabs.length - 3} more` : ''}
</div>
</div>
`).join('');
} catch (error) {
console.error('Error loading groups:', error);
}
}
function truncateTitle(title, maxLength = 40) {
return title.length > maxLength ? title.substring(0, maxLength) + '...' : title;
}
async function organizeTabs() {
organizeBtn.disabled = true;
organizeBtn.innerHTML = '<span class="spinner"></span>Analyzing...';
try {
const response = await chrome.runtime.sendMessage({ action: 'organizeTabs' });
if (response.success) {
groupsListEl.innerHTML = response.groups.map(group => `
<div class="group-item">
<div class="group-header">
<span class="group-name">${group.name}</span>
<span class="group-count">${group.tabs.length} tabs</span>
</div>
<div class="group-tabs">
${group.tabs.slice(0, 3).map(t => `• ${truncateTitle(t.title)}`).join('<br>')}
${group.tabs.length > 3 ? `<br>...and ${group.tabs.length - 3} more` : ''}
</div>
</div>
`).join('');
await loadStats();
} else {
alert('Error organizing tabs: ' + response.error);
}
} catch (error) {
console.error('Error:', error);
alert('An error occurred while organizing tabs');
}
organizeBtn.disabled = false;
organizeBtn.textContent = '🧠 Auto-Organize Tabs';
}
async function createNewGroup() {
const name = prompt('Enter group name:');
if (!name) return;
const tabs = await chrome.tabs.query({ currentWindow: true, active: true });
const activeTab = tabs[0];
if (!activeTab) {
alert('No active tab found');
return;
}
try {
const response = await chrome.runtime.sendMessage({
action: 'createGroup',
data: {
name: name,
tabIds: [activeTab.id],
color: 'grey'
}
});
if (response.success) {
await loadGroups();
await loadStats();
}
} catch (error) {
console.error('Error creating group:', error);
}
}
async function clearAllGroups() {
if (!confirm('Are you sure you want to clear all tab groups?')) return;
try {
await chrome.runtime.sendMessage({ action: 'clearGroups' });
groupsListEl.innerHTML = '<div class="loading">Click "Auto-Organize" to analyze and group your tabs</div>';
await loadStats();
} catch (error) {
console.error('Error clearing groups:', error);
}
}
});
The popup JavaScript handles user interactions and communicates with the background service worker. It loads statistics, displays existing groups, and provides buttons for organizing tabs, creating new groups, and clearing groups.
Step 5: Content Script for Advanced Analysis
The content script runs on web pages and extracts rich metadata for better AI analysis. This enables our extension to understand page content beyond just the title and URL.
// content.js - Extract page content for AI analysis
// Listen for messages from the background script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'extractContent') {
const content = extractPageContent();
sendResponse(content);
}
return true;
});
function extractPageContent() {
// Extract various content elements for analysis
const content = {
// Basic page info
title: document.title,
url: window.location.href,
domain: window.location.hostname,
// Meta tags
metaDescription: '',
metaKeywords: '',
ogTitle: '',
ogDescription: '',
ogImage: '',
// Content analysis
headings: [],
mainContent: '',
links: [],
// Semantic analysis
pageType: 'unknown',
primaryTopic: ''
};
// Get meta description
const metaDesc = document.querySelector('meta[name="description"]');
if (metaDesc) {
content.metaDescription = metaDesc.getAttribute('content') || '';
}
// Get meta keywords
const metaKeywords = document.querySelector('meta[name="keywords"]');
if (metaKeywords) {
content.metaKeywords = metaKeywords.getAttribute('content') || '';
}
// Get Open Graph tags
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle) {
content.ogTitle = ogTitle.getAttribute('content') || '';
}
const ogDesc = document.querySelector('meta[property="og:description"]');
if (ogDesc) {
content.ogDescription = ogDesc.getAttribute('content') || '';
}
const ogImage = document.querySelector('meta[property="og:image"]');
if (ogImage) {
content.ogImage = ogImage.getAttribute('content') || '';
}
// Get all headings for content analysis
const headingElements = document.querySelectorAll('h1, h2, h3');
content.headings = Array.from(headingElements).map(h => h.innerText.trim()).slice(0, 10);
// Get main content (simplified approach)
const body = document.body;
if (body) {
// Remove script and style elements
const clone = body.cloneNode(true);
const scripts = clone.querySelectorAll('script, style, nav, header, footer, aside');
scripts.forEach(el => el.remove());
// Get text content
content.mainContent = clone.innerText.substring(0, 2000).trim();
}
// Get all links (for domain analysis)
const linkElements = document.querySelectorAll('a[href]');
const links = Array.from(linkElements).map(a => a.href).slice(0, 20);
content.links = links;
// Determine page type based on content
content.pageType = detectPageType(content);
// Extract primary topic
content.primaryTopic = extractPrimaryTopic(content);
return content;
}
function detectPageType(content) {
const text = `${content.title} ${content.metaDescription} ${content.ogTitle} ${content.mainContent}`.toLowerCase();
const pageTypes = {
'E-commerce': ['shop', 'buy', 'cart', 'product', 'price', 'checkout', 'order', 'store'],
'Social Media': ['profile', 'friend', 'post', 'share', 'follow', 'like', 'comment', 'timeline'],
'Video/Media': ['video', 'watch', 'stream', 'movie', 'play', 'channel', 'subscribe'],
'News': ['news', 'article', 'headline', 'breaking', 'report', 'journal', 'coverage'],
'Documentation': ['docs', 'documentation', 'api', 'reference', 'guide', 'tutorial', 'manual'],
'Blog': ['blog', 'post', 'author', 'comment', 'read', 'article'],
'Forum': ['forum', 'thread', 'reply', 'topic', 'discussion', 'post'],
'Search': ['search', 'results', 'query', 'find'],
'Email': ['inbox', 'mail', 'email', 'message', 'compose', 'sent'],
'Productivity': ['document', 'spreadsheet', 'presentation', 'sheet', 'slide', 'doc']
};
for (const [type, keywords] of Object.entries(pageTypes)) {
const matchCount = keywords.filter(kw => text.includes(kw)).length;
if (matchCount >= 2) {
return type;
}
}
return 'General';
}
function extractPrimaryTopic(content) {
// Simple keyword extraction
const text = `${content.title} ${content.metaDescription} ${content.headings.join(' ')}`.toLowerCase();
const commonWords = new Set([
'the', 'and', 'is', 'in', 'at', 'of', 'for', 'on', 'with', 'a', 'an',
'to', 'be', 'or', 'not', 'are', 'from', 'by', 'as', 'it', 'this',
'that', 'you', 'we', 'they', 'can', 'will', 'has', 'have', 'been'
]);
// Extract words
const words = text.split(/\W+/).filter(w => w.length > 3);
const wordFreq = {};
words.forEach(word => {
if (!commonWords.has(word)) {
wordFreq[word] = (wordFreq[word] || 0) + 1;
}
});
// Get top words
const sorted = Object.entries(wordFreq).sort((a, b) => b[1] - a[1]);
return sorted.slice(0, 3).map(([word]) => word).join(', ');
}
// Notify background script that content is ready
setTimeout(() => {
chrome.runtime.sendMessage({
action: 'contentReady',
url: window.location.href
}).catch(() => {
// Ignore errors if background script is not available
});
}, 1000);
The content script extracts rich metadata from web pages including meta tags, Open Graph data, headings, and main content. This enables the AI to make more accurate categorization decisions based on actual page content rather than just URLs.
Step 6: Adding Icons
Every extension needs icons. For a production extension, you would create proper icon files, but for development, we can use placeholder icons. Chrome requires at least 16x16, 48x48, and 128x128 pixel icons.
Create a simple placeholder icon or use any PNG icon you have. The manifest references icons in the icons folder. You will need to create these icon files for the extension to load without errors.
Testing Your Extension
Now that we have built all the components, let’s test our AI Tab Organizer extension:
- Open Chrome and navigate to
chrome://extensions/ - Enable Developer mode in the top right corner
- Click Load unpacked and select your extension folder
- The extension icon should appear in your Chrome toolbar
- Open multiple tabs across different categories (work, social, shopping, etc.)
- Click the extension icon to open the popup
- Click “Auto-Organize Tabs” and watch the AI categorize and group your tabs
You should see your tabs automatically organized into color-coded groups based on their content and purpose.
Advanced Features to Consider
Once you have the basic extension working, here are some advanced features you can add:
Machine Learning Integration
Replace the keyword-based categorization with a proper machine learning model. You could use TensorFlow.js to run a classifier directly in the browser, or integrate with APIs like:
- Google Cloud Natural Language API
- AWS Comprehend
- OpenAI API for zero-shot classification
Tab Suspending
Integrate with the tabSuspender functionality to automatically suspend tabs in groups that haven’t been used recently, saving significant memory.
Sync Across Devices
Use Chrome’s sync storage to save tab groups and preferences across devices, so your organization scheme follows you everywhere.
Custom Rules
Allow users to define custom rules for categorization, giving them fine-grained control over how tabs are grouped.
Conclusion
Building an AI-powered tab organizer is a fantastic project that combines practical utility with modern web development techniques. In this guide, we covered:
- Setting up a Manifest V3 Chrome extension
- Creating an intuitive popup interface
- Implementing AI-powered tab categorization using keyword and domain analysis
- Managing Chrome tab groups programmatically
- Extracting page content for better analysis
- Testing and debugging your extension
The extension we built uses intelligent pattern matching to automatically categorize tabs without requiring external AI APIs, making it fast and privacy-friendly. Users can organize dozens of tabs in seconds with a single click, dramatically improving their browsing productivity.
With this foundation, you can extend the extension with machine learning, cloud sync, custom rules, and more advanced features. The Chrome extension platform provides powerful APIs that enable you to build sophisticated productivity tools that millions of users can benefit from.
Ready to take your tab management to the next level? Start building your AI-powered tab organizer today and experience the future of browser productivity.