How to Use Favicons in Chrome Extensions: Complete Guide (2026)

AppBooster Team · · 4 min read
Browser icons and favicons on a screen

What Are Favicons and Why Do Extensions Need Them?

Favicons are the small icons that identify websites in browser tabs, bookmarks, and history. Chrome extensions frequently need to display these icons — think tab managers, bookmark tools, history dashboards, or any extension that lists websites.

Chrome provides a built-in Favicon API specifically for extensions, letting you retrieve any website’s favicon without making external requests.


Setting Up the Favicon Permission

Step 1: Request the Permission

Add the favicon permission to your manifest.json:

{
  "manifest_version": 3,
  "name": "My Tab Manager",
  "version": "1.0",
  "permissions": ["favicon"],
  "action": {
    "default_popup": "popup.html"
  }
}

Note: The favicon permission only triggers a user-facing warning if you haven’t already declared tabs permission or host permissions. In most real-world extensions, you’ll have one of those, so the favicon permission adds no extra friction.

Step 2: Configure Web Accessible Resources (for Content Scripts)

If you need favicons inside content scripts, declare the favicon folder as web-accessible:

{
  "web_accessible_resources": [
    {
      "resources": ["_favicon/*"],
      "matches": ["<all_urls>"],
      "extension_ids": ["*"]
    }
  ]
}

Building the Favicon URL

Chrome exposes favicons through a special internal URL pattern:

chrome-extension://YOUR_EXTENSION_ID/_favicon/?pageUrl=WEBSITE_URL&size=SIZE

The cleanest way to construct this URL is with chrome.runtime.getURL():

function getFaviconUrl(pageUrl, size = 32) {
  const url = new URL(chrome.runtime.getURL('/_favicon/'));
  url.searchParams.set('pageUrl', pageUrl);
  url.searchParams.set('size', String(size));
  return url.toString();
}

Available Sizes

SizeBest For
16Compact lists, table rows
32Standard lists, cards
64Large displays, hero sections
128Detail views, previews

Chrome will return the closest available size. If the site doesn’t have a favicon at the requested size, Chrome scales the nearest match.


Practical Implementation: Tab Manager

Here’s a complete popup that lists all open tabs with their favicons:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { width: 350px; font-family: system-ui; padding: 12px; }
    .tab-item {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 8px;
      border-radius: 6px;
      cursor: pointer;
    }
    .tab-item:hover { background: #f0f0f0; }
    .tab-item img { width: 20px; height: 20px; }
    .tab-item span {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      flex: 1;
    }
  </style>
</head>
<body>
  <div id="tabs"></div>
  <script src="popup.js"></script>
</body>
</html>
// popup.js
function getFaviconUrl(pageUrl, size = 32) {
  const url = new URL(chrome.runtime.getURL('/_favicon/'));
  url.searchParams.set('pageUrl', pageUrl);
  url.searchParams.set('size', String(size));
  return url.toString();
}

async function renderTabs() {
  const tabs = await chrome.tabs.query({});
  const container = document.getElementById('tabs');

  tabs.forEach((tab) => {
    const item = document.createElement('div');
    item.className = 'tab-item';
    item.innerHTML = `
      <img src="${getFaviconUrl(tab.url)}" alt="" />
      <span>${tab.title || tab.url}</span>
    `;
    item.addEventListener('click', () => {
      chrome.tabs.update(tab.id, { active: true });
      chrome.windows.update(tab.windowId, { focused: true });
    });
    container.appendChild(item);
  });
}

renderTabs();

Handling Missing Favicons

Not every site has a favicon. Handle fallbacks gracefully:

function createFaviconImage(pageUrl) {
  const img = document.createElement('img');
  img.src = getFaviconUrl(pageUrl);
  img.alt = '';
  img.width = 20;
  img.height = 20;

  img.addEventListener('error', () => {
    // Fallback to a default globe icon
    img.src = chrome.runtime.getURL('icons/default-globe.png');
  });

  return img;
}

Alternatively, generate letter-based avatars as fallbacks:

function getLetterAvatar(url) {
  const domain = new URL(url).hostname.replace('www.', '');
  const letter = domain.charAt(0).toUpperCase();
  const canvas = new OffscreenCanvas(32, 32);
  const ctx = canvas.getContext('2d');

  ctx.fillStyle = '#6366f1';
  ctx.beginPath();
  ctx.arc(16, 16, 16, 0, Math.PI * 2);
  ctx.fill();

  ctx.fillStyle = '#ffffff';
  ctx.font = 'bold 18px system-ui';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText(letter, 16, 17);

  return canvas.convertToBlob().then(blob => URL.createObjectURL(blob));
}

Caching Favicons for Performance

If your extension displays many favicons frequently, cache them to avoid redundant lookups:

const faviconCache = new Map();

async function getCachedFavicon(pageUrl, size = 32) {
  const cacheKey = `${pageUrl}:${size}`;

  if (faviconCache.has(cacheKey)) {
    return faviconCache.get(cacheKey);
  }

  const url = getFaviconUrl(pageUrl, size);
  faviconCache.set(cacheKey, url);
  return url;
}

For persistent caching across service worker restarts, use chrome.storage.local.


What’s Next

Favicons are a small detail that significantly improves the polish and usability of any extension that works with websites. Get the basics right with Chrome’s built-in API, add fallback handling, and your users will notice the difference.

Looking for more ways to improve your extension’s user experience? ExtensionBooster offers free developer tools to analyze and optimize your Chrome Web Store listing.

Share this article

Build better extensions with free tools

Icon generator, MV3 converter, review exporter, and more — no signup needed.

Related Articles