Configure webpack to output images/fonts in a separate subfolders

I managed configure webpack to output CSS and JS into respective sub-directories, i.e. public/asests/css and public/assets/js. However, I don't know how to do the same for images and fonts.

In other words, I want to output images in public/assets/images folder and fonts into public/assets/fonts folder.

Here's my webpack config file:

var path = require('path');
var ExtractCSS = require('extract-text-webpack-plugin');

module.exports = {
    context: path.resolve('private/js'),
    resolve: ['', '.js', '.jsx', '.es6', '.json'],
    entry: {
        homepage: './homepage'
    },
    output: {
        path: path.resolve('public/assets'),
        publicPath: '/public/assets/',
        filename: 'js/[name].js'
    },
    plugins: [
        new ExtractCSS('css/[name].css')
    ],
    devServer: {
        contentBase: 'public'
    },
    module: {
        loaders: [
            {
                test: /\.(es6|js|jsx)$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
            {
                test: /\.css$/,
                exclude: /node_modules/,
                loader: ExtractCSS.extract('style-loader', 'css-loader')
            },
            {
                test: /\.less$/,
                exclude: /node_modules/,
                loader: ExtractCSS.extract('style-loader', 'css-loader!less-loader')
            },
            {
                test: /\.(jpg|jpeg|gif|png|woff|woff2|eot|ttf|svg)$/,
                exclude: /node_modules/,
                loader: 'url-loader?limit=1024'
            }
        ]
    }
}

I could figure this out by referring to url-loader & file-loader documentation on GitHub.

All, I needed to do was to add a name query-string parameter in loader to specify full path. I also learned that you can specify how files should be named in output location.

{
    test: /\.(jpg|jpeg|gif|png)$/,
    exclude: /node_modules/,
    loader:'url-loader?limit=1024&name=images/[name].[ext]'
},
{
    test: /\.(woff|woff2|eot|ttf|svg)$/,
    exclude: /node_modules/,
    loader: 'url-loader?limit=1024&name=fonts/[name].[ext]'
}

{ 
    test: /\.(ttf|eot|svg|woff2?)(\?v=[a-z0-9=\.]+)?$/i,
    include: folders.npm,
    loader: 'file?name=fonts/[name].[ext]'
},
{
    test: /\.(jpe?g|png|gif|svg|ico)$/i,
    include: folders.src,
    loaders: [
        'file?name=images/[sha512:hash:base64:7].[ext]',
        'image-webpack?progressive=true&optimizationLevel=7&interlaced=true'
    ]
}

This is what I use in production. I often come across situation where *.svg pictures are used and SVG fonts for IE fallback. Here I assume font are always inside node_modules. I have also seen devs doing test: /fonts\/[w+].(svg|eot ....).


Webpack 5 case-

Keeping it here just in case someone is looking for a Webpack 5 solution to the requirement of outputting image/font assets into separate directories into the output path.

Webpack 5 replaces raw-loader, url-loader and file-loader with Assets Module and it has 4 new modules to handle assets. Read in more details here - https://webpack.js.org/guides/asset-modules/

To our problem statement, the following configuration outputs all fonts/svgs into the directory fonts. But if certain files are less than 8KB, it throws them inline.

  {
    test: /\.(woff(2)?|eot|ttf|otf|svg|)$/,
    type: 'asset',   // <-- Assets module - asset
    parser: {
      dataUrlCondition: {
        maxSize: 8 * 1024 // 8kb
      }
    },
    generator: {  //If emitting file, the file path is
      filename: 'fonts/[hash][ext][query]'
    }
  }

Similarly we could do the same for images -

  {
    test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
    type: 'asset/resource',  //<-- Assets module - asset/resource
    generator: {
      filename: 'images/[hash][ext][query]'
    }
  }

Outputs all images to images directory irrespective of file size.