manifestfaviconwebpack-4react-16

react app favicon / manifest not being displayed on gh-pages


I have react/webpack app hosted on gh-pages. Favicon and Manifest icons are not showing.

favicons, manifest folder and manifest.webmanifest file are placed in client/assets folder.

< favicon setup >,

  1. app.js

import './assets/favicons/favicon.ico';

  1. Webpack.config
 test: /\.(jpe?g|png|gif|ico)$/,
            loader: 'file-loader',
            options: {
              name: devMode ? '[name].[ext]' : '[name].[hash].[ext]',
            },

new HTMLWebpackPlugin({
      template: './public/index.html',
      favicon: './client/assets/favicons/favicon.ico',
    }),
  1. index.html

< link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon.png" /> < link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" /> < link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" /> < link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" /> < link rel="shortcut icon" href="/favicon.ico" type="image/png/ico" />

< manifest setup >

  1. index.html

< link rel="manifest" href="/client/assets/manifest.json" />

  1. webpack config

[ EDITION ] I added CopyWebpackPlugin to copy manifest folder and manifest.json to dist folder. Now Manifest.json file is valid. but the icon is still not showing on homescreen.

webpack.base.js

const webpack = require('webpack');
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const autoprefixer = require('autoprefixer');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const NODE_ENV = process.env.NODE_ENV;
const devMode = NODE_ENV !== 'production';
const isTest = NODE_ENV === 'test';

const babelConfig = require('./.babelrc.js');

module.exports = {
  output: {
    filename: devMode ? 'bundle.js' : 'bundle.[hash].js',
    chunkFilename: devMode
      ? '[name].lazy-chunk.js'
      : '[name].lazy-chunk.[hash].js',
    path: path.resolve(__dirname, 'public/dist'),
    publicPath: '/',
  },
  resolve: { extensions: ['.js', '.jsx', '.json'] },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: [
          {
            loader: 'babel-loader',
            options: babelConfig,
          },
        ],
      },
      {
        test: /\.(sa|sc|c)ss$/,
        exclude: /node_modules/,
        use: [
          {
            loader: devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              importLoaders: 1,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              indent: 'postcss',
              plugins: [
                autoprefixer({
                  browsers: 'last 2 versions',
                }),
              ],
              sourceMap: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              includePaths: ['client/styles/main.scss'],
            },
          },
        ],
      },
      {
        test: /\.html$/,
        loader: 'html-loader',
        options: {
          attrs: ['img:src'],
        },
      },
      {
        test: /\.(jpe?g|png|gif|ico)$/,
        loader: 'file-loader',
        options: {
          name: devMode ? '[name].[ext]' : '[name].[hash].[ext]',
        },
      },
      {
        test: /\.svg$/,
        loader: 'file-loader',
        options: {
          name: devMode ? '[name].[ext]' : '[name].[hash].[ext]',
        },
      },
    ],
  },
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  plugins: [
    new CleanWebpackPlugin(['public/dist']),
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: JSON.stringify(NODE_ENV),
      },
    }),
    new HTMLWebpackPlugin({
      template: './public/index.html',
      favicon: './static/favicons/favicon.ico',
    }),
    new MiniCssExtractPlugin({
      filename: devMode ? '[name].css' : '[name].[chunkhash].css',
      chunkFilename: devMode ? '[id].css' : '[id].[chunkhash].css',
    }),
    new CaseSensitivePathsPlugin(),
    new CopyWebpackPlugin([
      { from: `${__dirname}/static`, to: `${__dirname}/public/dist` },
    ]),
    isTest
      ? new BundleAnalyzerPlugin({
          generateStatsFile: true,
        })
      : null,
  ].filter(Boolean),
};

webpack.prod.js

const merge = require('webpack-merge');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const cssnano = require('cssnano');
const TerserPlugin = require('terser-webpack-plugin');
const BrotliPlugin = require('brotli-webpack-plugin');
const baseConfig = require('./webpack.base');

const config = {
  mode: 'production',
  entry: './client/index.js',
  devtool: 'source-map',
  optimization: {
    minimize: true,
    minimizer: [
      new OptimizeCssAssetsPlugin({
        assetNameRegExp: /\.optimize\.css$/g,
        cssProcessor: cssnano,
        cssProcessorOptions: {
          discardComments: { removeAll: true },
        },
        canPrint: true,
      }),
      new TerserPlugin({
        test: /\.js(\?.*)?$/i,
        exclude: /node_modules/,
        terserOptions: {
          ecma: 6,
          compress: true,
          output: {
            comments: false,
            beautify: false,
          },
        },
      }),
    ],
    runtimeChunk: {
      name: 'manifest',
    },
  },
  plugins: [new BrotliPlugin()],
};

module.exports = merge(config, baseConfig);

Solution

  • If you want to show icon on browser. Please try to do this in index.html

    <link rel="shortcut icon" href="%PUBLIC_URL%/assets/favicons/favicon.ico">