DEV Community

webdiscus
webdiscus

Posted on • Edited on

Automatically generate favicons and inject them into HTML with Webpack

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. The webmanifest file with favicons should be generated automatically too.

Solution

For Webpack we can use the powerful html-bundler-webpack-plugin.

This bundler plugin renders a HTML template and can generate favicons automatically. Under the hood is used the 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
Enter fullscreen mode Exit fullscreen mode

Optional, you can install CSS and SASS loaders if you use them:

npm i -D css-loader sass-loader
Enter fullscreen mode Exit fullscreen mode

Usage

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

<!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>
Enter fullscreen mode Exit fullscreen mode

Create minimalistic Webpack config:

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',
      },
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

See complete favicons options here.

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:

<!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>
Enter fullscreen mode Exit fullscreen mode

The generated dist/img/favicons/manifest.webmanifest will be looks like:

{
  "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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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

Give HTML Bundler Plugin for Webpack a ⭐️ on GitHub

Top comments (0)