Your chrome extension needs two sets of icons

chrome extension

Having only one set of icons for your extension, doesn't provide the best user experience. Do you know that you can change the icon of your extensions dynamically?

Usual setup

Usually when developers build their extensions, they have 1 set of extension icons in multiple sizes. This is good. But if your extension doesn't work on all sites, then this doesn't provide the best user experience. Also know that chrome extensions don't work on the following urls.

  1. chrome://
  2. https://chrome.google.com/webstore
  3. https://chromewebstore.google.com/

A visual cue to the user on whether the extension can be used on a particular site goes a long way in ensuring good user experience.

Even if your extension works on all sites, it's better to have other set of icons as extensions are not supported on some sites (A few are listed above).

{
    "icons": {
        "16": "images/icon_16.png",
        "48": "images/icon_48.png",
        "128": "images/icon_128.png"
    }
}

You can define the default icons as usual in the manifest.json file as shown above. Though you can set both the active & inactive icons dynamically, you will have to define one set of icons in the manifest.json file. This is required by the Chrome web store.

Dynamically setting the extension icons

To achieve this, you need to have a background script. If you don't have one already, create one and add it to the manifest.json.

Have 2 sets of icons ready, before we proceed. One for active and other for inactive state. Create 2 objects in the background script with the icons.

Here's how it will look. The path has to be relative from the extension directory.

const supportedIcon = {
  16: 'images/icon_16.png',
  48: 'images/icon_48.png',
  128: 'images/icon_128.png'
};

const unsupportedIcon = {
  16: 'images/icon-dark_16.png',
  48: 'images/icon-dark_48.png',
  128: 'images/icon-dark_128.png'
};

Here images directory is present in the extension directory. This is the parent directory that has the extension code and the manifest.json file.

Code to dynamically set the icon. Add this to the background script.

function isUrlSupported(url) {
  if (url.startsWith('https://chrome.google.com/') || url.startsWith('https://chromewebstore.google.com') || url.startsWith('brave://')) {
    return false;
  }

  const matches = chrome.runtime.getManifest().content_scripts[0].matches;

  return matches.some(match => {
    const regex = new RegExp('^' + match.replace(/\*/g, '.*') + '$');
    return regex.test(url);
  });
}

// Listen for tab updates
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete') {
    if (isUrlSupported(tab.url)) {
      chrome.action.setIcon({
        path: supportedIcon,
        tabId: tabId
      });
    } else {
      chrome.action.setIcon({
        path: unsupportedIcon,
        tabId: tabId
      });
    }
  }
});

In the above code, I am fetching the matches value from content_scripts in the manifest.json and updating the icons accordingly. So the sites to which I inject the content_scripts I show the supported icons and for others I show the unsupported set of icons.

You can update the logic in the function isUrlSupported. In the example above, I am checking the matches value in content_scripts. You can check other values depending on your extension. One example is to check the values from host_permissions array.

Conclusion

Having 2 sets of icons is not mandatory, but improves the user experience greatly. Go ahead and update your extensions to support this.