Chrome Extension Content Blocker — Developer Guide

4 min read

Build a Content Blocker Extension — Full Tutorial

What We’re Building

manifest.json

{
  "manifest_version": 3,
  "name": "Focus Blocker",
  "version": "1.0.0",
  "permissions": ["declarativeNetRequest", "storage", "alarms", "activeTab"],
  "action": { "default_popup": "popup.html" },
  "background": { "service_worker": "background.js" },
  "declarative_net_request": {
    "rule_resources": [{ "id": "default_rules", "enabled": true, "path": "rules.json" }]
  },
  "web_accessible_resources": [{
    "resources": ["blocked.html"],
    "matches": ["<all_urls>"]
  }]
}

Step 1: Static Rules (rules.json)

Step 2: Dynamic Rules for User-Added Sites

async function addBlockedSite(domain) {
  const ruleId = await getNextRuleId();
  await chrome.declarativeNetRequest.updateDynamicRules({
    addRules: [{
      id: ruleId,
      priority: 1,
      action: { type: "redirect", redirect: { extensionPath: "/blocked.html" } },
      condition: { urlFilter: `||${domain}`, resourceTypes: ["main_frame"] }
    }]
  });
  // Save to storage
  const storage = createStorage(defineSchema({ blockedSites: 'string' }), 'sync');
  const sites = JSON.parse(await storage.get('blockedSites') || '[]');
  sites.push({ domain, ruleId });
  await storage.set('blockedSites', JSON.stringify(sites));
}

Step 3: Blocked Page (blocked.html)

Step 4: Popup UI

Step 5: Scheduling with chrome.alarms

// background.js
chrome.alarms.create("startBlocking", { when: getNextWorkStart() });
chrome.alarms.create("stopBlocking", { when: getNextWorkEnd() });

chrome.alarms.onAlarm.addListener(async (alarm) => {
  if (alarm.name === "startBlocking") enableBlocking();
  if (alarm.name === "stopBlocking") disableBlocking();
});

Step 6: Enable/Disable Blocking

async function enableBlocking() {
  await chrome.declarativeNetRequest.updateEnabledRulesets({
    enableRulesetIds: ["default_rules"]
  });
  chrome.action.setBadgeText({ text: "ON" });
  chrome.action.setBadgeBackgroundColor({ color: "#e74c3c" });
}

async function disableBlocking() {
  await chrome.declarativeNetRequest.updateEnabledRulesets({
    disableRulesetIds: ["default_rules"]
  });
  chrome.action.setBadgeText({ text: "" });
}

Step 7: Storage with @theluckystrike/webext-storage

const storage = createStorage(defineSchema({
  blockedSites: 'string',   // JSON array
  workStart: 'number',      // Hour (0-23)
  workEnd: 'number',        // Hour (0-23)
  isEnabled: 'boolean',
  totalBlockedToday: 'number'
}), 'sync');

Testing

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

No previous article
No next article