declarativeContent Permission

6 min read

declarativeContent Permission

What It Grants

Access to the chrome.declarativeContent API for showing/hiding your extension’s action icon based on page content — without needing to read page data.

Manifest

{
  "permissions": ["declarativeContent"]
}

User Warning

None — this permission does not trigger a warning. This is a key advantage: it doesn’t require host permissions.

API Access

Core Concept

Instead of reading every page and deciding whether to show your icon, you declare rules that Chrome evaluates internally — no extension wake-up needed.

Basic Usage: Show Action on Matching Pages

How to Use declarativeContent API: Show Action on Matching Pages

chrome.runtime.onInstalled.addListener(() => {
  // First, disable the action by default
  chrome.action.disable();

  // Then define rules for when to enable it
  chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
    chrome.declarativeContent.onPageChanged.addRules([
      {
        conditions: [
          new chrome.declarativeContent.PageStateMatcher({
            pageUrl: { hostSuffix: '.github.com' }
          })
        ],
        actions: [new chrome.declarativeContent.ShowAction()]
      }
    ]);
  });
});

PageStateMatcher Conditions

URL Matching

// Match by host
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { hostEquals: 'developer.chrome.com' }
});

// Match by host suffix (subdomains)
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { hostSuffix: '.google.com' }
});

// Match by URL prefix
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { urlPrefix: 'https://docs.google.com/' }
});

// Match by scheme
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { schemes: ['https'] }
});

// Match by path
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { pathEquals: '/settings' }
});

CSS Matching

// Show action only when page has a video element
new chrome.declarativeContent.PageStateMatcher({
  css: ['video']
});

// Show when page has a specific class
new chrome.declarativeContent.PageStateMatcher({
  css: ['div.article-content']
});

// Combine URL and CSS
new chrome.declarativeContent.PageStateMatcher({
  pageUrl: { hostSuffix: '.youtube.com' },
  css: ['video.html5-main-video']
});

Multiple Rules

chrome.declarativeContent.onPageChanged.addRules([
  {
    conditions: [
      new chrome.declarativeContent.PageStateMatcher({
        pageUrl: { hostSuffix: '.github.com' }
      }),
      new chrome.declarativeContent.PageStateMatcher({
        pageUrl: { hostSuffix: '.gitlab.com' }
      }),
      new chrome.declarativeContent.PageStateMatcher({
        pageUrl: { hostSuffix: '.bitbucket.org' }
      })
    ],
    actions: [new chrome.declarativeContent.ShowAction()]
  }
]);

Actions Available

ShowAction

Shows the extension’s action icon (enables it).

new chrome.declarativeContent.ShowAction()

SetIcon

Changes the icon dynamically.

new chrome.declarativeContent.SetIcon({
  imageData: { '16': imageData16, '32': imageData32 }
})

RequestContentScript

Injects a content script on matching pages.

new chrome.declarativeContent.RequestContentScript({
  js: ['content-script.js'],
  css: ['styles.css']
})

Storage Integration

import { createStorage, defineSchema } from '@theluckystrike/webext-storage';

const schema = defineSchema({ enabledSites: 'string' }); // JSON array
const storage = createStorage(schema, 'sync');

async function updateRules() {
  const sites = JSON.parse(await storage.get('enabledSites') || '["github.com"]');

  chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
    const conditions = sites.map((site: string) =>
      new chrome.declarativeContent.PageStateMatcher({
        pageUrl: { hostSuffix: site }
      })
    );
    chrome.declarativeContent.onPageChanged.addRules([{
      conditions,
      actions: [new chrome.declarativeContent.ShowAction()]
    }]);
  });
}

Comparison with tabs.onUpdated

| Feature | declarativeContent | tabs.onUpdated | |—|—|—| | Permission warning | None | “Read your browsing history” (with tabs) | | Wakes service worker | No | Yes | | CPU usage | Minimal | Per-navigation | | Flexibility | URL/CSS patterns only | Full programmatic control |

When to Use

When NOT to Use

Permission Check

import { checkPermission } from '@theluckystrike/webext-permissions';
const granted = await checkPermission('declarativeContent');

Cross-References

Frequently Asked Questions

How does declarativeContent work in Manifest V3?

DeclarativeContent allows your extension to take actions based on page content without needing to run content scripts constantly. Use chrome.declarativeContent.onPageChanged to define rules.

Is declarativeContent still available in MV3?

Yes, but with limitations. It works for page actions and can show the action based on page conditions, but cannot automatically inject content scripts. —

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

No previous article
No next article