Browser Extension Builder Toolkit
Enterprise-grade skill for expert, building, browser, extensions. Includes structured workflows, validation checks, and reusable patterns for utilities.
Browser Extension Builder
A browser extension development skill for building Chrome, Firefox, and cross-browser extensions with manifest v3, content scripts, background workers, and popup interfaces.
When to Use
Choose Browser Extension Builder when:
- Creating Chrome or Firefox extensions with manifest v3
- Building content scripts that modify web pages or extract data
- Developing popup UIs, options pages, and background service workers
- Porting extensions between Chrome and Firefox with cross-browser compatibility
Consider alternatives when:
- Building a full web application — deploy as a standard web app
- Needing server-side processing — build a web service with a simple browser bookmarklet
- Creating a simple page script — use a userscript manager like Tampermonkey
Quick Start
# Project structure for a Chrome extension mkdir -p my-extension/{src,public,dist}
// manifest.json (Manifest V3) { "manifest_version": 3, "name": "My Extension", "version": "1.0.0", "description": "A useful browser extension", "permissions": ["storage", "activeTab", "scripting"], "action": { "default_popup": "popup.html", "default_icon": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" } }, "background": { "service_worker": "background.js", "type": "module" }, "content_scripts": [ { "matches": ["<all_urls>"], "js": ["content.js"], "css": ["content.css"], "run_at": "document_idle" } ], "options_page": "options.html" }
// background.ts - Service worker chrome.runtime.onInstalled.addListener(({ reason }) => { if (reason === 'install') { chrome.storage.local.set({ settings: { enabled: true, theme: 'auto' } }); } }); chrome.action.onClicked.addListener(async (tab) => { if (!tab.id) return; await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: () => { document.body.classList.toggle('extension-active'); } }); }); // Message handling between components chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'GET_DATA') { fetchData(message.url).then(sendResponse); return true; // Keep channel open for async response } }); async function fetchData(url: string) { const response = await fetch(url); return response.json(); } // content.ts - Content script function injectUI() { const container = document.createElement('div'); container.id = 'my-extension-root'; container.style.cssText = 'position:fixed;top:10px;right:10px;z-index:99999;'; document.body.appendChild(container); // Send message to background chrome.runtime.sendMessage( { type: 'GET_DATA', url: window.location.href }, (response) => { container.innerHTML = `<div class="ext-widget">${response.title}</div>`; } ); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', injectUI); } else { injectUI(); }
Core Concepts
Extension Architecture
| Component | File | Role | Access |
|---|---|---|---|
| Service Worker | background.js | Event handling, API calls | Chrome APIs, no DOM |
| Content Script | content.js | Page DOM modification | Page DOM, limited Chrome APIs |
| Popup | popup.html/js | User interface (action click) | Chrome APIs, own DOM |
| Options Page | options.html/js | Extension settings | Chrome APIs, own DOM |
| DevTools Panel | devtools.html/js | Developer tools integration | Inspected page, Chrome APIs |
| Side Panel | sidepanel.html/js | Persistent side panel | Chrome APIs, own DOM |
Cross-Browser Compatibility
// browser-polyfill wrapper for Chrome/Firefox compatibility const browserAPI = typeof browser !== 'undefined' ? browser : chrome; // Storage abstraction async function getStorage(keys: string[]): Promise<Record<string, any>> { return new Promise((resolve) => { browserAPI.storage.local.get(keys, resolve); }); } async function setStorage(data: Record<string, any>): Promise<void> { return new Promise((resolve) => { browserAPI.storage.local.set(data, resolve); }); } // Tab query abstraction async function getActiveTab() { const tabs = await browserAPI.tabs.query({ active: true, currentWindow: true }); return tabs[0]; }
Configuration
| Option | Description | Default |
|---|---|---|
manifest_version | Manifest version (v2 deprecated, use v3) | 3 |
target_browsers | Target browsers: chrome, firefox, both | "chrome" |
content_script_matches | URL patterns for content script injection | ["<all_urls>"] |
permissions | Required browser permissions | ["storage","activeTab"] |
build_tool | Bundler: webpack, vite, rollup | "vite" |
framework | UI framework: react, vue, svelte, vanilla | "react" |
hot_reload | Enable hot reload during development | true |
source_maps | Generate source maps for debugging | true |
Best Practices
- Request minimal permissions and use
activeTabinstead of broad host permissions whenever possible — users are more likely to install extensions that request fewer permissions, and Chrome Web Store reviews are faster - Use Chrome's
storage.localAPI instead oflocalStoragein service workers because service workers do not have access tolocalStorageand the storage API provides better cross-component access - Handle service worker lifecycle properly since manifest v3 service workers can be terminated at any time; do not store state in global variables, use
chrome.storagefor persistence, and re-register listeners on each service worker startup - Isolate content script styles using Shadow DOM or highly specific CSS selectors to prevent your extension's styles from affecting the host page and vice versa
- Test with the built-in Chrome extension debugger by loading your extension unpacked, using the service worker inspector for background script debugging, and the page inspector for content scripts
Common Issues
Service worker becoming inactive: Manifest v3 service workers are terminated after 30 seconds of inactivity, losing all in-memory state. Store important data in chrome.storage, use chrome.alarms for periodic tasks instead of setInterval, and re-initialize listeners at the top level of your service worker script.
Content script CSP conflicts: Some websites have strict Content Security Policies that block inline scripts and styles injected by extensions. Use the scripting API with executeScript instead of inline event handlers, load CSS from extension files rather than injecting inline styles, and use the world: 'MAIN' option when you need to access the page's JavaScript context.
Cross-origin requests blocked in content scripts: Content scripts inherit the page's CORS restrictions. Make cross-origin API calls from the service worker using chrome.runtime.sendMessage to relay requests, or declare host permissions in the manifest for specific domains you need to access.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.