Build Your Own Extensions Switcher: A Step‑by‑Step GuideManaging browser extensions can become a productivity sink: too many add-ons slow the browser, create conflicts, or expose privacy risks; but disabling useful tools means losing functionality you rely on. An extensions switcher — a small utility that lets you quickly enable, disable, and group extensions — solves that by putting control into your hands. This guide walks through building a cross‑browser extensions switcher as a browser extension (Chrome/Chromium and Edge) and a Firefox add‑on, with tips for UI, storage, permissions, and testing.
What you’ll build
You’ll create a browser extension that:
- Lists all installed extensions/add‑ons.
- Lets users enable/disable individual extensions.
- Saves and applies named “profiles” (groups of extension states).
- Provides a compact UI (popup) and optional options page for managing profiles.
- Works in Chromium‑based browsers and Firefox with minimal code differences.
Tools & tech:
- WebExtension APIs (compatible across Chromium and Firefox).
- HTML/CSS/JavaScript for UI.
- Manifest V3 for Chromium; Manifest V2 or adapted MV3 for Firefox depending on browser support.
- Optional build tools: webpack/rollup for bundling.
Architecture overview
At a high level:
- Manifest declares permissions and resources.
- Popup UI shows extension list and quick actions.
- Background/service worker performs enable/disable operations and stores profiles.
- Options page provides profile creation/editing and import/export.
Key considerations:
- Permissions: access to manage other extensions requires explicit permissions (e.g., “management” in Chromium).
- Security: only request necessary permissions and explain them in the extension description.
- Cross‑browser differences: Firefox’s management API behavior can differ; use conditional logic where needed.
Step 1 — Set up project and manifests
Create a folder structure:
- manifest.json (or manifest_v3.json)
- popup.html, popup.js, popup.css
- background.js (or service_worker.js for MV3)
- options.html, options.js, options.css
- icons/
Example Chromium Manifest V3 (manifest.json):
{ "manifest_version": 3, "name": "Extensions Switcher", "version": "1.0", "description": "Quickly enable/disable browser extensions and manage profiles.", "permissions": ["management", "storage"], "action": { "default_popup": "popup.html", "default_icon": "icons/icon48.png" }, "background": { "service_worker": "background.js" }, "options_ui": { "page": "options.html", "open_in_tab": true }, "icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" } }
Firefox notes:
- Firefox currently supports Manifest V2 and has MV3 support evolving; if MV3 issues arise, use MV2 background scripts and adjust manifest_version accordingly.
- Ensure “management” permission is allowed — some browsers prompt users more strictly.
Step 2 — Background logic & permissions
The background script handles reading installed extensions, toggling them, and saving/loading profiles. Use the management API.
Example background functions (simplified):
// background.js async function listExtensions() { const items = await chrome.management.getAll(); // filter out themes, the extension itself, and other irrelevant items return items.filter(e => e.type === 'extension' && !e.isApp && e.id !== chrome.runtime.id); } async function setExtensionEnabled(id, enabled) { return chrome.management.setEnabled(id, enabled); } async function saveProfile(name, states) { const data = await chrome.storage.local.get('profiles') || {}; data.profiles = data.profiles || {}; data.profiles[name] = states; await chrome.storage.local.set(data); } async function loadProfiles() { const { profiles } = await chrome.storage.local.get('profiles') || {}; return profiles || {}; }
Edge cases:
- Some extensions are “installed by policy” or integral; attempting to disable them may fail. Handle errors gracefully and notify users.
- Extensions that are disabled for the browser profile may still show in the list; use the enabled property.
Step 3 — Popup UI: list and quick toggles
Design goals:
- Fast access: list extensions with toggle switches.
- Search/filter input.
- Quick apply/save profile actions.
Basic popup.html structure:
<!doctype html> <html> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="popup.css" /> </head> <body> <input id="search" placeholder="Search extensions..." /> <div id="ext-list"></div> <div class="actions"> <button id="save-profile">Save Profile</button> <button id="apply-profile">Apply Profile</button> </div> <script src="popup.js"></script> </body> </html>
popup.js essentials:
async function renderList() { const list = await chrome.runtime.sendMessage({ action: 'listExtensions' }); const container = document.getElementById('ext-list'); container.innerHTML = ''; list.forEach(ext => { const row = document.createElement('div'); row.className = 'ext-row'; row.innerHTML = ` <img src="${ext.icons?.[0]?.url || 'icons/icon48.png'}" class="icon" /> <span class="name">${ext.name}</span> <label class="switch"> <input type="checkbox" data-id="${ext.id}" ${ext.enabled ? 'checked' : ''}/> <span class="slider"></span> </label>`; container.appendChild(row); }); container.querySelectorAll('input[type=checkbox]').forEach(cb => { cb.addEventListener('change', async e => { const id = e.target.dataset.id; await chrome.runtime.sendMessage({ action: 'setEnabled', id, enabled: e.target.checked }); }); }); } document.addEventListener('DOMContentLoaded', renderList);
Include simple CSS for readability and touch targets.
Step 4 — Profiles: save, apply, import/export
Profile structure: { name: string, states: { [extensionId]: boolean } }.
Saving current state:
// in popup.js async function captureCurrentState() { const list = await chrome.runtime.sendMessage({ action: 'listExtensions' }); const states = {}; list.forEach(e => { states[e.id] = e.enabled; }); return states; } document.getElementById('save-profile').addEventListener('click', async () => { const name = prompt('Profile name:'); if (!name) return; const states = await captureCurrentState(); await chrome.runtime.sendMessage({ action: 'saveProfile', name, states }); alert('Saved'); });
Applying a profile:
// background.js handles bulk apply to avoid UI blocking async function applyProfile(states) { for (const [id, enabled] of Object.entries(states)) { try { await chrome.management.setEnabled(id, enabled); } catch (err) { console.warn('Failed to set', id, err); } } }
Import/export:
- Store profiles as JSON in chrome.storage; provide import/export via file input/download link using Blob.
Step 5 — Options page & profile management
Options page shows saved profiles with edit/delete/rename and import/export controls.
Key features:
- Preview profile (list which extensions will be enabled/disabled).
- Bulk apply and schedule (optional).
- Share profiles (export JSON).
Example options.js actions:
- loadProfiles() -> populate UI
- deleteProfile(name)
- renameProfile(old, new)
- exportProfile(name) -> download JSON
- importProfiles(file) -> parse and validate JSON, then save
Step 6 — Cross‑browser adjustments
Chromium (Chrome/Edge):
- MV3 service worker background script; use chrome.management and chrome.storage.
- Manifest V3 required for new Chrome extensions.
Firefox:
- If MV3 unsupported, use background scripts (MV2) or adapt service worker usage.
- API differences:
- chrome.management in Firefox may not expose icons similarly; fetch icons safely.
- Some management operations may require additional user confirmations.
- Test and provide fallback messages if an API call is not available.
Feature detection:
const mgmt = chrome.management || browser.management; if (!mgmt) { // show message about limited support }
Step 7 — Security, privacy and permissions UX
- Request only “management” and “storage”. Explain in the extension’s description and options UI why management is needed (to toggle other extensions).
- Do not collect or transmit extension data externally. Keep profiles local; if offering cloud sync, make it explicit and opt‑in.
- Handle errors when attempting to disable extensions installed by policy or browser components.
- Consider a read‑only mode for users who prefer not to grant management permission; display a helper that links to native browser extension settings.
Step 8 — Testing & packaging
Testing checklist:
- Install in developer mode (Chrome/Edge: load unpacked; Firefox: about:debugging).
- Verify list accuracy, toggle behavior, profile save/apply, import/export.
- Test with extensions that are enabled/disabled, installed by policy, or are themes.
- Test across multiple browser profiles and OSes (Windows/macOS/Linux).
Packaging:
- Follow each browser’s store requirements (icons, descriptions, privacy policy).
- For Chrome Web Store and Edge Add‑ons, ensure MV3 compliance.
- For Firefox Add‑ons, sign and submit through AMO.
Advanced ideas & enhancements
- Keyboard shortcuts to apply profiles quickly.
- Context menu for quick enable/disable from toolbar.
- Scheduling: automatically switch profiles at certain times or on network changes.
- Per‑profile rules: enable extensions only on specific domains.
- Cloud sync (encrypted) for multi‑device profiles.
- Analytics (local only) to help users see which profiles they use most — opt‑in only.
Sample repo structure & minimal code pointers
- README.md (permissions explanation, usage)
- manifest.json
- popup.html / popup.js / popup.css
- background.js
- options.html / options.js / options.css
- icons/*
Minimal message passing example:
// background.js chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { if (msg.action === 'listExtensions') { chrome.management.getAll().then(items => sendResponse(items)); return true; // async } if (msg.action === 'setEnabled') { chrome.management.setEnabled(msg.id, msg.enabled).then(() => sendResponse({ok:true})).catch(err => sendResponse({ok:false,err:err.message})); return true; } });
Conclusion
Building an extensions switcher teaches practical WebExtension skills: permission handling, background messaging, cross‑browser compatibility, and UI/UX tradeoffs. Start small (list + toggle), then add profiles, import/export, and polishing touches like keyboard shortcuts and scheduling. Test across browsers and be transparent about permissions to earn user trust.
Leave a Reply