It is time to rethink how we cook a set of favicons for modern browsers and stop the icon generator madness. Currently, frontend developers 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.

The favicons proved to be a more exhaustive topic that anyone could wish for, so I also summarized a whole article in just two code snippets for those who suffered enough and know exactly what to do. Still, I recommend geeking out to the rest of it!

Extremely short version

Instead of serving dozens of icons, all you need is just five icons and one JSON file.

In your HTML, for the browser:

<link rel="icon" href="/favicon.ico"><!-- 32×32 -->
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- 180×180 -->
<link rel="manifest" href="/manifest.webmanifest">

And in your web app manifest:

// manifest.webmanifest
{
  "icons": [
    { "src": "/192.png", "type": "image/png", "sizes": "192x192" },
    { "src": "/512.png", "type": "image/png", "sizes": "512x512" }
  ]
}

That is it. If you want to know how I arrived at this, which compromises I had to take, and how to build a set like this from scratch in a step-by-step fashion, tune in for the rest of the article.

Long version, where everything is explained

“Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.”

—Antoine de Saint-Exupéry, Airman’s Odyssey

A concept of a favicon, short for “favorite icon”, have been around since the early 2000s. We all see those cute little images in your browser’s tab bar that help tell open websites apart every day. 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 always had some aesthetic beef with icons that don’t come from Cupertino and downplayed favicons in Safari for years, had finally given up and now displays them properly 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 is common to offload the grueling task of generating necessary files for the ever-growing list of screens and devices to favicon generator tools. No human in their right mind would want to spend hours creating them by hand. We are here to build websites, after all, not make browser vendors happy.

A set of favicons generated by a popular online generator
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?

So, I set out to create a minimal list of favicons that will work in all cases and in all browsers, barring some edge cases where it 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 are concerned about performance, I am 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 are not 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. It solves a problem for Internet users that don’t have unlimited data plans.

Here comes the minimal set of icons that I came up with in my research and practice. It should work with all popular browsers and devices, 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 (becomes blurry, for instance). In that case, you can ask your designer to come up with a special version of the logo that is tailored to fit small pixel grids.

Don’t get smart with 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.

II. A single SVG icon with light/dark version for modern browsers

SVG is a vector format that describes curves instead of pixels. On large sizes, it is more efficient than raster images. 72% of all browsers support SVG icons as of this writing.

Your HTML page should have a <link> tag in its <head> with rel="icon"type="image/svg+xml" and href with a link to SVG file as attributes.

SVG is an XML format and can contain a <style> tag that describes CSS. As 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

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 in a <head>.

iPads since iOS 8+ require a 180×180 resolution. Other devices will downscale the image, but if we provide the source with a good-enough quality, the downscaling won’t hurt the end-user (I’ll come back to it later).

Small note: an Apple touch icon will look better if you add a 20px padding around the icon and put some background color. You can use any image editor for that.

IV. Web app manifest with 192×192 and 512×512 PNG icons for Android devices.

  • Web app manifest is a JSON file with all details for a browser to install your website as a system application. The format came from Google for its PWA initiative.
  • Your HTML page should have a <link rel="manifest" href="path.webmanifest"> tag with a link to manifest file.
  • Manifest should have an icon field that links to two icons: 192×192 displayed on a home screen and 512×512 that will be used as a splash screen as PWA is loading.
{
  "icons": [
    { "src": "/192.png", "type": "image/png", "sizes": "192x192" },
    { "src": "/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 less successful formats out there.

Windows Tile Icon

Microsoft Edge used to support a special icon format to pin websites to Start Menu. For recent versions of Windows, this is no longer required.

Safari Pinned Icon

Safari used to have a separate requirement to display an icon in 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">

Be warned that shortcut is not, and never was a valid link relation. Read this amazing article by Mathias Bynens from ten years ago that explains why we never needed shortcuts and rel="icon" is just fine.

Yandex Tableau

Yandex Browser is a Chromium-based browser from the biggest Russian search engine. In Russia, it has a 20% market share. It has a nice feature that allows website to display live data in widgets on a home screen through a special JSON manifest provided by the yandex-tableau-widget link. However, the feature proved not to be very popular, and now Yandex has removed the technical documentation for it from its website. Regular icon manifests will work just as well.

Opera Coast

Opera Coast, an experimental browser for iOS, used to require a special 228×228 icon. The browser left the App Store in 2017, and I doubt it has survived multiple iOS updates since then.

Now, as we waved good-bye to 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 is easy to adjust the SVG size in any SVG editor. In Inkscape, you can change document size in File → Document Properties and center the logo using Object → Align and Distribute.

Save your file as icon.svg. Now let’s fiddle with our SVG, so it plays well with modern system themes. Ask your designer how the colors should be inverted in 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 dark or missing fill. Add a CSS media query that triggers 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>

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 width and height to 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 terminal:

inkscape ./icon.svg --export-width=32 --export-filename="./tmp.png"
convert ./tmp.png ./favicon.ico
tm ./tmp.png

Scale the image down to 16×16 and check icon visibility. If it became 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:

  1. Open favicon.ico with 32×32 icon.
  2. Create a new layer with 16×16 size.
  3. Put a 16×16 version of an icon into this layer.
  4. Export the file. GIMP will save each layout as a separate version of an icon.

Or you can do the same in ImageMagick by:

convert ./icon32.png ./icon16.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 export as apple-touch-icon.png.

Or you can do the same in Inkscape by:

inkscape ./icon.svg --export-width=512 --export-filename="./icon-512.png"
inkscape ./icon.svg --export-width=192 --export-filename="./icon-192.png"

Step 4: Optimize PNG and SVG files

The best tool to optimize SVGs is SVGO. Run:

npx svgo --multipass icon.svg

Squoosh is a great web app to optimize raster images.

  1. Open your icon-512.png in Squoosh.
  2. Change Compress setting to OxiPNG.
  3. Enable “Reduce palette”.
  4. Set 64 colors.
  5. Compare before/after by moving a slider. If you see a difference, increase the number of colors.
  6. Save the file.

Repeat these steps for icon-192.png and apple-touch-icon.png.

Step 5: Add 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">
+ <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 file’s hash in a name as a fingerprint). If you are using Webpack with [html-webpack-plugin]:

  1. Create index.html template.
  2. Add template to plugin options:
    new HtmlWebpackPlugin({ template: "./view/index.html" });
    
  3. Define HTML template with links (examples use ERB used to include files, but 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">
        <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>
    
  4. Use copy-webpack-plugin to copy favicon.ico without adding a hash to file name.

Free Tip: Separate icon for staging

Favicons are a nice way to distinguish your production environment from a staging one. I find using an alternative icon for staging super effective in avoiding expensive confusions.

Create a favicon-dev.ico with the same logo, but invert the colors (or do something else that makes sense for you). Same for SVG, create icon-dev.svg.

Now, replace icons in your HTML template depending on 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">
+     <link rel="icon" 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 app 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-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">
  <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('./icon512.png'), sizes: '512x512 }
      ]
    })
  ]

Thank you for reading! As you can see, with modern web standards, the task of creating an 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 be even more amazing! Feel free to ping me on Twitter if you’re willing to build one; I will be more than happy to help!

2021年 favicon 的设置(英文)