How to Favicon in 2024: Three files that fit most needs
Translations
It’s time to rethink how we cook a set of favicons for modern browsers and stop the icon generator madness. Frontend developers currently have to deal with 20+ static PNG files just to display a tiny website logo in a browser tab or on a touchscreen. Read on to see how to take a smarter approach and adopt a minimal set of icons that fits most modern needs.
Irina Nazarova CEO at Evil Martians
The topic of favicons has proven to be more exhaustive than anyone could’ve ever wished, so I’ve also summarized the entire article in just two code snippets for those who’ve already suffered enough and who know exactly what to do. Still, I recommend geeking out to the rest of it!
Icon transcendence: customizing icons to complement fonts
The extremely short version
Instead of serving dozens of icons, all you need is just five icons and one JSON file.
For the browser using HTML:
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- 180×180 -->
If you’re making a PWA (Progressive Web App), also add this to the HTML:
<link rel="manifest" href="/manifest.webmanifest">
And a file with the web app manifest:
// manifest.webmanifest
{
"icons": [
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/icon-mask.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" },
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}
Maskable icons should have bigger paddings. The safe zone is a 409×409 circle. Use maskable.app to check your icon.
That’s it. If you want to know how I got here, the compromises I had to make, and how to build a set like this from scratch in a step-by-step fashion, stay tuned in for the rest of the article.
The long version, where everything is explained
The concept of a favicon, which is short for “favorite icon”, have been around since the early 2000s. We’ve all seen those cute little images in our browser’s tab bar which help us differentiate our open websites. Users expect your website to have a favicon. It’s one of those little things that make other people take you seriously.
Even Apple, which has always had some kind of aesthetic beef with icons that don’t come from Cupertino, downplaying favicons in Safari for years, has finally given up and now properly displays them across all of its devices.
If you have a public-facing website, it has to have a favicon. Sadly, what users perceive as one icon is actually a lot of them.
So, it’s common to offload the grueling task of generating these necessary files for an ever-growing list of screens and devices to favicon generator tools. No one in their right mind would ever want to spend hours creating them by hand. We’re here to build websites, after all, not to make browser vendors happy.
A set of favicons generated by a popular online generator
As a creator of NanoID and a proponent of minimalistic open source, I tend to think in a slightly different direction. What is the most efficient set of website icons? Which formats are outdated? Which icon types can be replaced with small compromises?
Thus, I set out to create a minimal list of favicons that will work in all cases and in all browsers—barring some edge cases—and even then, this will still work, just not 100% perfectly.
The Ultimate Favicon Setup
Instead of creating many images with different sizes, I decided to rely on SVG and browser downscaling. If you’re concerned about performance, I’m here to set the record straight:
- Browsers download favicons in the background, so a bigger favicon image does not affect website performance.
- SVG is a great way to reduce image size for images that aren’t supposed to be bitmaps in the first place; for many logos the resulting file will be much smaller than a PNG.
- With just three PNG images in this minimum set, you can use advanced tools to optimize their size. This solves a problem for Internet users that don’t have unlimited data plans.
Now I’ll reveal the minimal set of icons that I came up with during my research and practice. This list should work with all popular browsers and devices, both old and new.
I. favicon.ico for legacy browsers
ICO files actually have a directory structure and can pack files with different resolutions. I recommend sticking to a single 32×32 image, unless the one you have doesn’t downscale well to 16×16 (if it becomes blurry, for instance). In that case, you can ask your designer to come up with a special version of the logo that’s tailored to fit small pixel grids.
Don’t get smart with the folder static asset folder structure and cache busters. https://example.com
website should have a favicon on https://example.com/favicon.ico
. Some tools, like RSS readers, just request /favicon.ico
from the server and don’t bother looking elsewhere.
We need sizes="32x32"
for <link>
to .ico
file in order to fix the Chrome bug where it chooses an ICO file over an SVG.
II. A single SVG icon with a light/dark version for modern browsers
SVG is a vector format that describes curves instead of pixels. At large sizes, it’s more efficient than raster images. As of this writing, 72% of all browsers support SVG icons.
Your HTML page should have a <link>
tag in its <head>
with rel="icon"
, type="image/svg+xml"
and with the href
containing a link to the SVG file as attributes.
SVG is an XML format and can contain a <style>
tag to describes CSS. As with any CSS, it can contain media queries like @media (prefers-color-scheme: dark)
. This will allow you to toggle the same icon between light and dark system themes.
III. 180×180 PNG image for Apple devices
The Apple touch icon is an image that Apple devices will use if you add the webpage as a shortcut to your home screen on an iPhone or iPad. Your HTML page should have <link rel="apple-touch-icon" href="apple-touch-icon.png">
tag inside <head>
.
Since iOS 8+, iPads have required an image with a 180×180 resolution. Other devices will downscale it, but if we provide the source with a high-enough quality, the downscaling won’t hurt the end-user (I’ll come back to this later).
Small note: an Apple touch icon will look better if you place 20px
padding around the icon and add some background color. You can use any image editor to do this.
IV. Web app manifest with 192×192 and 512×512 PNG icons for Android devices
- A web app manifest is a JSON file containing all the details for a browser to install your website as a system application. This format came about from Google via its PWA initiative.
- Your HTML page should have a
<link rel="manifest" href="path.webmanifest">
tag with a link to the manifest file. - The manifest should have an
icon
field that links to three icons: 192×192, for display on the home screen, a 512×512 maskable icon for different Android launchers, and a 512×512 which will be used as a splash screen while the PWA is loading. - The maskable icon should have bigger paddings around the icon so it can be cropped by the launcher to fit its design. The safe zone is a central circle of 409×409. You can use maskable.app to check your icon.
{
"icons": [
{
"src": "/icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/icon-mask.png",
"type": "image/png",
"sizes": "512x512",
"purpose": "maskable"
},
{
"src": "/icon-512.png",
"type": "image/png",
"sizes": "512x512"
}
]
}
Did we forget anyone?
There are, of course, more favicon flavors out there, some of them quite obscure, so let’s see how our setup fares with them. Maybe, it’s time to say farewell to some of the less successful formats out there.
Windows Tile Icon
Microsoft Edge used to support a special icon format to pin websites to the start menu. For recent versions of Windows, this is no longer required.
Safari Pinned Icon
Safari formerly had a requirement of SVG monochrome icon for pinned tabs. However, since Safari 12, we can use a regular favicon for pinned tabs. Even apple.com doesn’t use the mask-icon
anymore.
rel=“shortcut”
A lot of (now outdated) tutorials will include a favicon.ico
into HTML like this:
<link rel="shortcut icon" href="/favicon.ico" sizes="32x32">
Be warned that shortcut
is not, and never has been, a valid link relation. Read this amazing article by Mathias Bynens from ten years ago that explains why we never needed shortcuts and why rel="icon"
is just fine.
Opera Coast
In the past, Opera Coast, an experimental browser for iOS, required a special 228×228 icon. This browser left the App Store in 2017, and I doubt it survived the multiple iOS updates since that time.
Now, as we wave good-bye to our fallen comrades, let’s see how to produce an ultimate favicon set for those who are still standing.
How to build our Ultimate Favicon Set
Here’s how to build our ultimate, minimalistic favicon set in six quick steps. All you need to start is an SVG file for the logo that you want to use.
Step 1: Prepare the SVG
Be sure that the SVG image is square. Open the source file in your system viewer and check the image’s width and height. It’s easy to adjust the SVG size using any SVG editor. In Inkscape, you can change document size by selecting File → Document Properties and then center the logo using Object → Align and Distribute.
Save your file as icon.svg
. Now let’s fiddle with our SVG and make it play well with modern system themes. Ask your designer how the colors should be inverted for a dark theme (for B&W logos, you just change black to white).
Now, open your SVG file in a text editor. Find a <path>
with a dark or missing fill
. Add a CSS media query that will trigger on theme changes and change fill
to the colors you want:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
+ <style>
+ @media (prefers-color-scheme: dark) {
+ .a { fill: #f0f0f0 }
+ }
+ </style>
- <path fill="#0f0f0f" d="…" />
+ <path class="a" fill="#0f0f0f" d="…" />
</svg>
You may also use this media query technique with SVGs to add wide-gamut P3 colors to your favicons.
Step 2: Create an ICO file
Open your icon.svg
file in a raster graphics editor. I recommend GIMP; it’s free and multi-platform.
Accept rendering SVG to raster. Set the width and height to be 32 pixels. Export file to favicon.ico
using 32 bpp, 8-bit alpha, no palette settings.
If you do not have GIMP you can install Inkscape and ImageMagick and convert SVG to ICO in the terminal:
inkscape ./icon.svg --export-width=32 --export-filename="./tmp.png"
# In Windows call `magick convert ./tmp.png ./favicon.ico`
convert ./tmp.png ./favicon.ico
rm ./tmp.png
Scale the image down to 16×16 and check the icon visibility. If it has become too blurry, it would be better to ask your designer for a custom tiny version of the logo.
To include a separate 16×16 version of an icon:
- Open
favicon.ico
with the 32×32 icon. - Create a new layer with a 16×16 size.
- Put the 16×16 version of an icon into this layer.
- Export the file. GIMP will save each layout as a separate version of the icon.
Or you can do the same in ImageMagick by:
convert ./icon-32.png ./icon-16.png ./favicon.ico
Step 3: Create PNG images
Open your source SVG file in a raster graphics editor again and create a 512×512 image. Export it as icon-512.png
.
Scale the image to 192×192 and export it to icon-192.png
. Now scale the image itself to 140×140 and set the canvas to 180×180, and then export it as apple-touch-icon.png
.
Then, scale it to 409×409 and increase the canvas to 512×512, and export it as icon-mask.png
. Check the icon in the maskable.app with different masks and tune the icon size if needed.
Or you can do the same in Inkscape:
inkscape --export-type="png" --export-width=512 --export-filename="./icon-512.png" ./icon.svg
inkscape --export-type="png" --export-width=192 --export-filename="./icon-192.png" ./icon.svg
Step 4: Optimize PNG and SVG files
The best tool for optimizing SVGs is SVGO. To use it, run:
npx svgo --multipass icon.svg
Squoosh is a great web app to optimize raster images:
- Open your
icon-512.png
in Squoosh. - Change the Compress setting to
OxiPNG
. - Enable “Reduce palette”.
- Set 64 colors.
- Compare the before/after by moving the slider. If you see a difference, increase the number of colors.
- Save the file.
Repeat these steps for icon-192.png
and apple-touch-icon.png
.
Step 5: Add the icons to HTML
You need to add links to favicon.ico
and to apple-touch-icon.png
into your HTML.
For static HTML:
<title>My website</title>
+ <link rel="icon" href="/favicon.ico" sizes="32x32">
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png">
However, we recommend using a bundler to generate cache busters (include the file’s hash in the name as a fingerprint). If you are using Webpack with HtmlWebpackPlugin
:
-
Create an
index.html
template. -
Add the template to the plugin options:
new HtmlWebpackPlugin({ template: "./view/index.html" });
-
Define an HTML template with links (the examples use
HtmlWebpackPlugin
to include files, but it can be your templating language of choice):<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>My website</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <link rel="icon" href="/favicon.ico" sizes="32x32"> <link rel="icon" type="image/svg+xml" href="<%= require('./icon.svg').default %>"> <link rel="apple-touch-icon" href="<%= require('./apple-touch-icon.png').default %>" > </head> <body></body> </html>
-
Use
copy-webpack-plugin
to copyfavicon.ico
without adding a hash to file name.
Bonus tip: create a separate icon for staging
Favicons are a nice way to distinguish your production environment from a staging one. I find that using an alternative icon for staging is super effective for avoiding any costly cases of confusion.
Create a favicon-dev.ico
with the same logo, but invert the colors (or do something that makes sense to you). Do the same for SVG and create icon-dev.svg
.
Now, replace the icons in your HTML template depending on the process.env.NODE_ENV === 'production'
condition:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My website</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
- <link rel="icon" href="/favicon.ico" sizes="32x32">
+ <link rel="icon" sizes="32x32" href="<%=
+ process.env.NODE_ENV === 'production'
+ ? '/favicon.ico'
+ : require('./favicon-dev.ico').default
+ %>">
<link rel="icon" type="image/svg+xml" href="<%=
- require('./icon.svg').default
+ process.env.NODE_ENV === 'production'
+ ? require('./icon.svg').default
+ : require('./icon-dev.svg').default
%>">
<link rel="apple-touch-icon" href="<%=
require('./apple-touch-icon.png').default
%>">
</head>
<body></body>
</html>
Step 6: Create a web manifest
For static HTML, create a JSON file named manifest.webmanifest
:
{
"name": "My website",
"icons": [
{ "src": "/icon-192.png", "type": "image/png", "sizes": "192x192" },
{ "src": "/icon-mask.png", "type": "image/png", "sizes": "512x512", "purpose": "maskable" },
{ "src": "/icon-512.png", "type": "image/png", "sizes": "512x512" }
]
}
Link it in your HTML:
<title>My website</title>
+ <link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
With Webpack, you can use webpack-pwa-manifest plugin:
plugins: [
…,
new WebpackPwaManifest({
name: 'My website',
icons: [
{ src: resolve('./icon-192.png'), sizes: '192x192' },
{ src: resolve('./icon-mask.png'), sizes: '512x512', purpose: 'maskable' },
{ src: resolve('./icon-512.png'), sizes: '512x512' }
]
})
]
Thank you for reading! As you can see, with modern web standards, the task of creating our Ultimate Favicon Set becomes quite straightforward. And even though following the steps manually shouldn’t take much of your time, having an automated tool to do the same will make things even more amazing! Feel free to ping me on Twitter if you’re willing to build one; I’ll be more than happy to help!
Changelog
2024-09-05
- Added maskable icon for PWA.
2023-11-19
- Fixed CLI commands for ICO and PNG generation.
2023-07-11
sizes="any"
for ICO file was replaced tosizes="32x32"
to fix bug when Chrome downloaded both ICO and SVG files.
2021-07-19
sizes="any"
was added to ICO file to fix bug when Chrome took ICO file instead of SVG.
2021-01-19
- Inkscape commands were added to convert SVG to PNG.
2021-01-15
- ImageMagick commands were added for users who do not have GIMP.