Automatically generate favicons and inject them into HTML with Webpack
Tags: webdev, javascript, html, frontend
Published: 2023-11-09 11:32:59

# Problem

We have a source image file, e.g., 1024x1024px, and we want to generate favicons of different sizes for various platforms, matching [web application manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest). The webmanifest file with favicons should be generated automatically too.

# Solution

For Webpack we can use the powerful [html-bundler-webpack-plugin](https://github.com/webdiscus/html-bundler-webpack-plugin).

This bundler plugin renders a HTML template and can generate favicons automatically. Under the hood is used the [Favicons](https://github.com/itgalaxy/favicons) Node.js module.
This module generates favicons and their associated files for many platforms such as `android`, `apple`, `windows`.

# Install

Install modules:
```
npm i -D html-bundler-webpack-plugin favicons
```

Optional, you can install CSS and SASS loaders if you use them:
```
npm i -D css-loader sass-loader
```

# Usage

For example, we have a simple HTML file, where we can specify source files of styles, scripts and images:

```html
<!DOCTYPE html>
<html>
<head>
  <!-- source favicon file relative to this HTML file -->
  <link href="./myFavicon.png" rel="icon" />
  
  <!-- SCSS file relative to this HTML file -->
  <link href="./styles.scss" rel="stylesheet" />
  
  <!-- source script file relative to this HTML file -->
  <script src="./main.js" defer="defer"></script>
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>
```

Create minimalistic Webpack config:

```js
const path = require('path');
// bundler plugin to render html
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');
// favicon plugin to generate icons
const { FaviconsBundlerPlugin } = require('html-bundler-webpack-plugin/plugins');

module.exports = {
  mode: 'production',

  output: {
    path: path.join(__dirname, 'dist/'),
  },

  plugins: [
    new HtmlBundlerPlugin({
      entry: {
        // define templates here
        index: './src/views/home.html', // => dist/index.html
      },
      js: {
        // output filename of JS
        filename: '[name].[contenthash:8].js',
      },
      css: {
        // output filename of CSS
        filename: '[name].[contenthash:8].css',
      },
    }),

    new FaviconsBundlerPlugin({
      enabled: 'auto', // true, false, auto - generate favicons in production mode only
      faviconOptions: {
        path: '/img/favicons', // output path
        appName: 'My App',
        icons: {
          android: true, // Create Android homescreen icon.
          appleIcon: true, // Create Apple touch icons.
          appleStartup: false, // Create Apple startup images.
          favicons: true, // Create regular favicons.
          windows: false, // Create Windows 8 tile icons.
          yandex: false, // Create Yandex browser icon.
        },
      },
    }),
  ],

  module: {
    rules: [
      {
        test: /\.(s?css)$/,
        use: ['css-loader', 'sass-loader'],
      },
      {
        test: /\.(png|jpe?g|ico|svg)$/,
        type: 'asset/resource',
      },
    ],
  },
};
```

See complete favicons options [here](https://github.com/itgalaxy/favicons#usage).

The generated HTML contains output filenames of CSS and JS, but the original tag `<link href="./myFavicon.png" rel="icon" />` is replaced with the generated favicon and meta tags.

The generated _dist/index.html_:
```html
<!DOCTYPE html>
<html>
<head>
  <!-- original tag is replaced with tags generated by favicons module -->
  <link rel="apple-touch-icon" sizes="1024x1024" href="/img/favicons/apple-touch-icon-1024x1024.png">
  <link rel="apple-touch-icon" sizes="114x114" href="/img/favicons/apple-touch-icon-114x114.png">
  <link rel="apple-touch-icon" sizes="120x120" href="/img/favicons/apple-touch-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/img/favicons/apple-touch-icon-144x144.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/img/favicons/apple-touch-icon-152x152.png">
  <link rel="apple-touch-icon" sizes="167x167" href="/img/favicons/apple-touch-icon-167x167.png">
  <link rel="apple-touch-icon" sizes="180x180" href="/img/favicons/apple-touch-icon-180x180.png">
  <link rel="apple-touch-icon" sizes="57x57" href="/img/favicons/apple-touch-icon-57x57.png">
  <link rel="apple-touch-icon" sizes="60x60" href="/img/favicons/apple-touch-icon-60x60.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/img/favicons/apple-touch-icon-72x72.png">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicons/apple-touch-icon-76x76.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/img/favicons/favicon-16x16.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/img/favicons/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="48x48" href="/img/favicons/favicon-48x48.png">
  <link rel="icon" type="image/x-icon" href="/img/favicons/favicon.ico">
  <link rel="manifest" href="/img/favicons/manifest.webmanifest">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="My App">
  <meta name="application-name" content="My App">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="theme-color" content="#fff">
  
  <link href="css/styles.05e4dd86.css" rel="stylesheet" />
  <script src="js/main.f4b855d8.js" defer="defer"></script>
</head>
<body>
  <h1>Hello World!</h1>
</body>
</html>
```

The generated _dist/img/favicons/manifest.webmanifest_ will be looks like:
```js
{
  "name": "My App",
  "short_name": "My App",
  "description": null,
  "dir": "auto",
  "lang": "en-US",
  "display": "standalone",
  "orientation": "any",
  "start_url": "/?homescreen=1",
  "background_color": "#fff",
  "theme_color": "#fff",
  "icons": [
    {
      "src": "/img/favicons/android-chrome-36x36.png",
      "sizes": "36x36",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-48x48.png",
      "sizes": "48x48",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-72x72.png",
      "sizes": "72x72",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-96x96.png",
      "sizes": "96x96",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-144x144.png",
      "sizes": "144x144",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-256x256.png",
      "sizes": "256x256",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-384x384.png",
      "sizes": "384x384",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/img/favicons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any"
    }
  ]
}
```

This is a game-changer for web developers, making the process of generating favicons for different platforms a breeze!

{% cta https://github.com/webdiscus/html-bundler-webpack-plugin %} Give HTML Bundler Plugin for Webpack a ⭐️ on GitHub {% endcta %}