How to Set and Change a Favicon with JavaScript
Set or swap a favicon with JavaScript by updating the link rel="icon" element. Covers SVG favicons, generated icons, caching, fallbacks, and preparing logo artwork for small sizes.
Contents
- The short answer
- Step 1: Keep a static favicon in the HTML
- Step 2: Write a reusable function to set the favicon
- Step 3: Change the favicon in response to an event
- Step 4 (optional): Generate an SVG favicon in code
- Step 5 (optional): Draw a badge with canvas for a raster fallback
- Step 6 (optional): Handle light and dark browser themes inside the SVG
- Dealing with caching
- Preparing logo artwork that works as a favicon
- How to verify the result
- Known gaps in this guide
The short answer
To set or change a favicon with JavaScript, find the page's link element whose rel attribute contains "icon". If none exists, create one. Then assign a new value to its href property. The browser reads the updated reference and redraws the icon in the tab. The script does not edit an image file. It only changes which image file the page points to.
A favicon is the small icon a browser shows in tabs, bookmarks and history lists. An SVG favicon is one supplied as Scalable Vector Graphics. SVG describes shapes as mathematical paths instead of a fixed grid of pixels, so it can be drawn sharply at any size. A raster favicon, such as PNG or ICO, stores pixels at fixed dimensions.
Step 1: Keep a static favicon in the HTML
Put a normal favicon declaration in the document head before you add any script. Several things may never run your JavaScript or see its changes, including bookmarks, saved shortcuts, crawlers and the first paint of the page. The static markup is your baseline. JavaScript only changes it while the page is open.
<link rel="icon" href="favicon.svg" type="image/svg+xml">
<link rel="icon" href="favicon.png" type="image/png">
Pair the SVG with a raster fallback. Support for SVG favicons has varied between browsers and versions, and the material behind this guide does not confirm which current browsers support them. Check your target browsers yourself rather than assuming the SVG will be used everywhere.
Step 2: Write a reusable function to set the favicon
This function updates every existing icon link so the browser cannot keep using an old entry. If the page has no icon link, the function creates one.
function setFavicon(href, type) {
let links = document.querySelectorAll('link[rel~="icon"]');
if (links.length === 0) {
const link = document.createElement('link');
link.rel = 'icon';
document.head.appendChild(link);
links = [link];
}
links.forEach((link) => {
if (type) {
link.type = type;
} else {
link.removeAttribute('type');
}
link.href = href;
});
}
Updating every matching link is a defensive choice. Suppose the page declares several icons and you change only one. The browser may keep showing a different entry, and the change will look like it failed. Another approach is to remove the extra links and keep one that you control.
Step 3: Change the favicon in response to an event
Call the function whenever the page state changes. Common triggers include a new notification, a finished upload, a status change, or the user switching away from the tab.
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
setFavicon('favicon-away.svg', 'image/svg+xml');
} else {
setFavicon('favicon.svg', 'image/svg+xml');
}
});
Step 4 (optional): Generate an SVG favicon in code
Use this when the icon itself has to change. Examples are an unread-count dot or a color that reflects status. Build the SVG markup as a string, wrap it in a Blob, and pass an object URL to the function. Revoke the previous object URL each time so old icons do not pile up in memory.
let currentIconUrl = null;
function setSvgFavicon(svgMarkup) {
const blob = new Blob([svgMarkup], { type: 'image/svg+xml' });
const url = URL.createObjectURL(blob);
setFavicon(url, 'image/svg+xml');
if (currentIconUrl) URL.revokeObjectURL(currentIconUrl);
currentIconUrl = url;
}
setSvgFavicon(`
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
<rect width='64' height='64' rx='12' fill='#1a1a2e'/>
<circle cx='48' cy='16' r='12' fill='#e63946'/>
</svg>`);
The xmlns value in the snippet is the standard SVG namespace identifier. It is required for the markup to be read as SVG. It is not a link to fetch. The viewBox attribute sets the internal coordinate system of the drawing. A square viewBox keeps the icon from being stretched or letterboxed in the square favicon slot.
Step 5 (optional): Draw a badge with canvas for a raster fallback
Some target browsers may not show SVG favicons. For those, draw the base icon onto a canvas element, add the badge, and export the result as PNG with the canvas toDataURL method. Wait until the source image has fully loaded before you draw it. If the source image comes from another origin and that server does not allow cross-origin use, the canvas becomes "tainted", and the browser will refuse to export it.
function setBadgedFavicon(baseSrc, showBadge) {
const img = new Image();
img.onload = () => {
const size = img.naturalWidth || 64;
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, size, size);
if (showBadge) {
ctx.fillStyle = '#e63946';
ctx.beginPath();
ctx.arc(size * 0.75, size * 0.25, size * 0.2, 0, Math.PI * 2);
ctx.fill();
}
setFavicon(canvas.toDataURL('image/png'), 'image/png');
};
img.src = baseSrc;
}
Step 6 (optional): Handle light and dark browser themes inside the SVG
You may not need JavaScript to follow the system theme. An SVG favicon can contain a style block with a prefers-color-scheme media query, which switches its own colors. Whether the browser honors that query inside a favicon depends on the browser. Test it, and keep a design that stays legible on both light and dark tab backgrounds.
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'>
<style>
path { fill: #1a1a2e; }
@media (prefers-color-scheme: dark) {
path { fill: #f1f1f1; }
}
</style>
<path d='M12 52 L32 12 L52 52 Z'/>
</svg>
Dealing with caching
Browsers often cache favicons aggressively. You may replace the file on the server and still see the old icon. When you switch between static files, add a version marker to the reference, such as a query string, so the browser treats it as a new resource. Blob object URLs and canvas data are new values on every call, so they skip this problem.
setFavicon('favicon.svg?v=2', 'image/svg+xml');
Preparing logo artwork that works as a favicon
The script only swaps references. How good the icon looks depends on the artwork. A full logo rarely reads well at tab size, so plan a simplified mark for this use.
- Use a symbol, monogram or single letterform instead of the full lockup. A lockup is the fixed arrangement of symbol and wordmark.
- Set a square artboard and a matching square viewBox, and leave consistent padding around the mark.
- Convert live text to outlines (paths). The favicon then does not depend on fonts being installed or loaded.
- Avoid hairline strokes and fine detail, which disappear or blur when the icon is drawn very small. Thicken strokes or turn them into filled shapes.
- Remove hidden layers, editor metadata and unused definitions so the SVG stays small and predictable.
- Check contrast against both light and dark tab backgrounds.
- Preview the mark at actual favicon size, not only zoomed in inside the design tool.
A file ending in .svg is not proof of clean vector artwork. An SVG can contain an embedded raster image that will not scale cleanly. Open the file and check that it is made of paths and shapes. The file format also says nothing about whether you may use the logo. Confirm that you have the right to use the mark before you deploy it.
How to verify the result
- Open the browser developer tools, inspect the document head, and confirm that the icon link's href changed after your trigger fired.
- Check the network panel to see that the new icon file was requested and returned without errors.
- Watch the tab itself, since the visible icon is the real test. Hard-reload if a cached icon persists.
- Test every browser you target, including any that may ignore SVG favicons and fall back to the raster version.
- Switch the system between light and dark themes to confirm the icon stays legible.
Known gaps in this guide
No confirmed evidence was available for exact browser support of SVG favicons, recommended pixel sizes for raster fallbacks, or how specific browsers handle color-scheme queries inside favicons. Treat these as things to check against current browser documentation and your own testing. The code above relies only on standard DOM, Blob and canvas behavior.