How to Use Favicons in Chrome Extensions: Complete Guide (2026)
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=SIZEThe 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
| Size | Best For |
|---|---|
16 | Compact lists, table rows |
32 | Standard lists, cards |
64 | Large displays, hero sections |
128 | Detail 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
I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
WXT vs Plasmo vs CRXJS vs Extension.js vs Bedframe. Real benchmarks, honest opinions, and the framework with 12K stars that's quietly dying.
5 Best Email Marketing Services to Grow Your Chrome Extension (2026)
Compare the top email marketing platforms for SaaS and Chrome extension developers. MailerLite, Mailchimp, Brevo, ActiveCampaign, and Drip reviewed.
15 Best Practices to Build a Browser Extension That Users Love (2026 Guide)
Master browser extension development in 2026. Manifest V3, security, performance, and UX best practices to build extensions users love.