pastebin.richardson.dev

WebP image output in a Webpack build

Posted Dec 8, 20253.9 KB • Markdown Print Raw

How to enable WebP-only image output in a Webpack build (TypeScript + React)

This setup converts .png/.jpg/.jpeg to WebP during the build and makes existing import x from "./img.png" usages end up pointing to WebP automatically (no fallback).

Step 1) Install the image tooling

Using imagemin:

npm i -D image-minimizer-webpack-plugin imagemin imagemin-webp

( ImageMinimizerWebpackPlugin supports multiple engines; this uses imagemin + imagemin-webp. (webpack ))


Step 2) Ensure Webpack emits image imports as files

Use Asset Modules (Webpack 5) to handle your imports. (webpack )

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpe?g)$/i,
        type: "asset/resource",
        generator: {
          // force webp extension in output filenames
          filename: "images/[name].[contenthash].webp",
        },
      },
    ],
  },
};

Step 3) Add a WebP generator via ImageMinimizerWebpackPlugin

Webpack’s ImageMinimizerWebpackPlugin supports a generator preset (e.g. "webp") using imageminGenerate and imagemin-webp. (webpack )

const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");

module.exports = {
  optimization: {
    minimizer: [
      "...",
      new ImageMinimizerPlugin({
        generator: [
          {
            preset: "webp",
            implementation: ImageMinimizerPlugin.imageminGenerate,
            options: {
              plugins: [["imagemin-webp", { quality: 80 }]],
            },
          },
        ],
      }),
    ],
  },
};

Step 4) Automatically rewrite .png/.jpg imports to request WebP (no code changes)

The plugin supports selecting a generator preset via an as=... query (e.g. ?as=webp). (webpack ) Instead of editing your TS/TSX imports, have Webpack rewrite requests at bundle time:

const webpack = require("webpack");

module.exports = {
  plugins: [
    new webpack.NormalModuleReplacementPlugin(
      /\.(png|jpe?g)(\?.*)?$/i,
      (resource) => {
        // append as=webp safely
        if (/\bas=webp\b/.test(resource.request)) return;
        resource.request += resource.request.includes("?") ? "&as=webp" : "?as=webp";
      }
    ),
  ],
};

Now your existing code like:

import hero from "./hero.png";

will resolve as if it were:

import hero from "./hero.png?as=webp";

…and the emitted file/URL will be WebP.


Step 5) Add TypeScript module declarations (if you don’t already have them)

// images.d.ts
declare module "*.png" { const src: string; export default src; }
declare module "*.jpg" { const src: string; export default src; }
declare module "*.jpeg" { const src: string; export default src; }

(You don’t need *.webp typings if your source imports remain .png/.jpg.)


Step 6) Build and verify

  • Run a production build
  • Confirm dist/images/...webp exists
  • Confirm your JS bundles reference .webp URLs

Notes (so you don’t get surprised)

  • This affects only images that go through Webpack’s module graph (TS/TSX imports, CSS url() processed by Webpack, etc.). Hardcoded strings like "/img/foo.png" won’t be rewritten.
  • “No fallback” means older browsers without WebP support will not display images.

If you paste your current module.rules for images (and whether you use file-loader or asset modules already), I can provide a drop-in diff against your existing config.

References
  1. ImageMinimizerWebpackPlugin | webpack
  2. Asset Modules