7

I want to generate webp files from jpg/png from webpack. To do that i using image-webpack-plugin (https://github.com/tcoopman/image-webpack-loader)

In the plugin documentation it's written that the webp additional optimizer "Compress JPG & PNG images into WEBP" (https://github.com/tcoopman/image-webpack-loader#usage) but after followed the documentation steps the conversion not work.

The files are exported in jpg but nothing is converted.

I've already followed these posts but i've don't understand how to translate in a "non-react" environment :

  1. Webpack imagemin plugin to compress jpg, png and create webp?

  2. Webpack (Encore): convert images to webp using image-webpack-loader

webpack.config.js

 { 
   test:/\.(gif|png|jpe?g|svg)$/i,
   use:[ 
      { 
         loader:'file-loader',
         options:{ 
            outputPath:'images',
            name:'[name].[ext]'
         }
      },
      { 
         loader:'image-webpack-loader',
         options:{ 
            mozjpeg:{ 
               progressive:true,
               quality:65
            },
            optipng:{ enabled: false },
            pngquant:{ quality: [ 0.65, 0.90 ], speed:4 },
            gifsicle:{ interlaced: false },
            webp:{ quality: 75 }
         }
      }
   ]
}

Is there a reliable and clean way to turn jpg / png files into webp via webpack ?

PedroZorus
  • 661
  • 1
  • 6
  • 21

4 Answers4

18

Finally, i've found a proper solution. For future people who will come here :

I no longer use image-webpack-loader but imagemin & imagemin-webp


Setting up :

  1. Verify you have imagemin & imagemin-webp installed before do anything.

  2. Create a webpack.config.prod.js file to create a specific image conversion script before the webpack build script.

Into the array ['src/images/*.{jpg,png}'] is the input, 'destination' the output. The output is via src to avoid to load unused stuff in the dist directory and it permit to a potential ejs plugin to require directly .webp files by a 'require' command.

const imagemin = require( "imagemin" )
const webp = require( "imagemin-webp" )

imagemin( ['src/images/*.{jpg,png}'], {
    destination: 'src/images',
    plugins: [
        webp( { quality: 60 } )
    ]
} )

  1. Create a "prescript" in package.json dedicated to the production
"scripts": {
    "preprod": "node webpack.config.prod.js",
    "prod": "npm webpack"
}

Sources :

Pre & Post Hooks

Imagemin Plugin

Imagemin Webp

PedroZorus
  • 661
  • 1
  • 6
  • 21
  • 2
    Yeah till now 2020 in google official documentation mark this as the recommended method for using webp with webpack – Juan Amador Sep 09 '20 at 03:48
  • @JuanAmador : From Aug, 25 2020 you can now also use AVIF format wich is better than WEBP ;) https://caniuse.com/avif – PedroZorus Sep 09 '20 at 12:54
  • @JuanAmador can you give us link with this recommendation? – Sonny D Mar 18 '21 at 09:19
  • Using [responsive-loader](https://www.npmjs.com/package/responsive-loader) for images in webpack config is more comfortable for me, because it creates webp copy of image and outputs images as usual file-loader. In addition to this it can create multiple sizes for each image, which can be used for srcSet attribute or responsive background-image style prop with media queries. – Alex Shul Mar 06 '22 at 19:51
  • 1
    It's not maintained anymore. The author recommends Squoosh. See https://github.com/imagemin/imagemin/issues/385 – Akshay Mar 13 '23 at 17:52
2

Try this. https://github.com/GaoYYYang/image-optimize-loader#3-transform-your-pngjpg-into-webp

When you enable compress.webp, it will transform your png/jpg into webp files, and there will be no png/jpg files generated. Your source code will directly use webp file instead of png/jpg.

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|webp|git|svg|)$/i,
        use: [
          {
            loader: `img-optimize-loader`,
            options: {
              compress: {
                // This will transform your png/jpg into webp.
                webp: true,
                disableOnDevelopment: true
              }
            },
          },
        ],
      },
    ],
  },
};
GaoYYYang
  • 21
  • 1
  • Appreciated! But I'd be wary of using this without having a jpg/png fallback... even with high support rate of 85% as of Oct 2020 (https://caniuse.com/webp). – RockyK Oct 28 '20 at 23:07
  • 1
    @GaoYYYang - your package `image-optimize-loader` is great! Related to what RockyKev pointed out, is there way to have both the .webp and .png/.jpg files? That way we can use the .png/.jpg files as a fallback if a user's browser doesn't support .webp? – Jon Eric Escobedo Jul 09 '21 at 00:03
0

You can use responsive loader. Steps for using from official doc:

  1. Add to webpack config:
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|webp)$/i,
        use: {
          loader: "responsive-loader",
          options: {
            // If you want to enable sharp support:
            adapter: require("responsive-loader/sharp"),
          },
        },
      },
    ],
  },
}
  1. Import images in your JavaScript files:
import responsiveImage from 'img/myImage.jpg?sizes[]=300,sizes[]=600,sizes[]=1024,sizes[]=2048';
import responsiveImageWebp from 'img/myImage.jpg?sizes[]=300,sizes[]=600,sizes[]=1024,sizes[]=2048&format=webp';

// Outputs
// responsiveImage.srcSet => '2fefae46cb857bc750fa5e5eed4a0cde-300.jpg 300w,2fefae46cb857bc750fa5e5eed4a0cde-600.jpg 600w,2fefae46cb857bc750fa5e5eed4a0cde-600.jpg 600w ...'
// responsiveImage.images => [{height: 150, path: '2fefae46cb857bc750fa5e5eed4a0cde-300.jpg', width: 300}, {height: 300, path: '2fefae46cb857bc750fa5e5eed4a0cde-600.jpg', width: 600} ...]
// responsiveImage.src => '2fefae46cb857bc750fa5e5eed4a0cde-300.jpg'
// responsiveImage.toString() => '2fefae46cb857bc750fa5e5eed4a0cde-300.jpg'
...
  <picture>
    <source srcSet={responsiveImageWebp.srcSet} type='image/webp' />
    <img
      src={responsiveImage.src}
      srcSet={responsiveImage.srcSet}
      width={responsiveImage.width}
      height={responsiveImage.height}
      sizes='(min-width: 1024px) 1024px, 100vw'
      loading="lazy"
    />
  </picture>
...
Alex Shul
  • 500
  • 7
  • 22
0

How to create a webp image in the same path as in the working directory.

  1. Create create-webp.mjs:

import imagemin from 'imagemin';
import imageminWebp from 'imagemin-webp';

import { promises as fsPromises } from 'node:fs';
import { promisify } from 'node:util';
import path from 'node:path';
import fs from 'graceful-fs';

const writeFile = promisify(fs.writeFile);

imagemin(['./src/img/**/*.{jpg,jpeg,png}'], {
    plugins: [
        imageminWebp({ quality: 100 }),
    ],
}).then(files => files
    .forEach(async v => {
        let source = path.parse(v.sourcePath);
        v.destinationPath = `${source.dir}/${source.name}.webp`;
        await fsPromises.mkdir(path.dirname(v.destinationPath), { recursive: true });
        await writeFile(v.destinationPath, v.data);
    })
);
  1. Run node ./create-webp.mjs before run webpack.
  2. Add to webpack.config:

{
    test: /\.(gif|png|jpe?g|svg|webp)$/i,
    type: 'asset/resource',
    generator: {
        filename: content => {
            return content.filename.replace('src/', '');
        },
    },
},
Bluorenge
  • 89
  • 2
  • 5