DEV Community

webdiscus
webdiscus

Posted on • Edited on

1

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

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay