Extensions Switcher Tips: Organize, Enable, and Disable Extensions Efficiently

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.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *