Webpack 5.106

Webpack 5.106 introduces plugin validation hooks, runtime style injection for CSS Modules, improved tree shaking for CommonJS, and experimental support for alternative JavaScript parsers.

Plugin Validation with compiler.hooks.validate

Webpack adds a new top level validate option to enable or disable schema validation for webpack configuration, plugin options, and loader options. When validation is disabled, webpack skips these checks, which can reduce overhead in some scenarios, but it also removes important guardrails. Invalid options may only fail later or lead to unexpected behavior.

This change also introduces an internal API so plugins can register validation through compiler.hooks.validate and run it with compiler.validate(...), allowing validation behavior to be consistently controlled by the single validate flag.

Defaults

The default value of validate depends on the build mode and whether experiments.futureDefaults is enabled:

Modeexperiments.futureDefaultsDefault validate
developmentfalsetrue
developmenttruetrue
productionfalsetrue
productiontruefalse

Config example

module.exports = {
  // Disable schema validation (webpack config, plugins, and loaders)
  validate: false,

  // ...rest of config
};

Plugin authoring example (respecting validate: false)

class MyPlugin {
  constructor(options = {}) {
    this.options = options;
  }

  apply(compiler) {
    compiler.hooks.validate.tap("MyPlugin", () => {
      compiler.validate(
        () => require("./schema/MyPlugin.json"),
        this.options,
        { name: "My Plugin", baseDataPath: "options" },
        (options) => require("./schema/MyPlugin.check")(options),
      );
    });

    // ...normal plugin logic here...
  }
}

module.exports = MyPlugin;

CSS Modules with Runtime Style Injection

Webpack now supports exportType: "style" for CSS Modules (when experiments.css: true is enabled), which allows CSS to be injected into the DOM as a <style> (HTMLStyleElement) directly from the webpack runtime. This covers the typical use case of style-loader, so it is no longer necessary to use style-loader to inject styles when using this mode.

Additionally, the CSS Module exports are preserved (for example, the class name mapping in *.module.css).

For CSP compatibility, when a nonce has been configured in the webpack runtime (__webpack_require__.nc), the <style> injected by this mode receives the same nonce via the nonce attribute (webpack reuses the nonce provided by the application; it does not generate one automatically).

module.exports = {
  experiments: { css: true },
  module: {
    rules: [
      {
        test: /\.css$/,
        type: "css/module",
        parser: {
          exportType: "style",
        },
      },
    ],
  },
};

Better Tree Shaking for CommonJS Destructuring

Webpack can now statically analyze destructuring assignments directly from CommonJS require (and module.require) and treat only the destructured properties as “referenced exports”, instead of conservatively assuming the whole exports object is used. This improves dead-code elimination in optimized builds and can reduce bundle size in codebases that still consume CommonJS modules.

Examples

// Only `a` is considered referenced
const { a } = require("./module");
// Also supported
const { a, b } = module.require("./module");

Context Support for VirtualUrlPlugin

VirtualUrlPlugin (via webpack.experiments.schemes.VirtualUrlPlugin) now supports a context option that defines the base directory used to resolve relative imports inside virtual modules. This feature is currently experimental, as it is part of the experiments.schemes API.

This makes virtual modules behave more like real files: code such as import "./utils" resolves consistently instead of falling back to compiler.context and potentially resolving incorrectly.

context can be set per virtual module (inside the module definition) or as a plugin level default. It defaults to "auto", which tries to infer the context from the virtual module id or path; otherwise it falls back to compiler.context. Conceptually, when you set context for a module, webpack treats that virtual module as if it lived inside that directory for resolving relative paths.

For example, if you define a virtual module id virtual/table.js with context: path.join(__dirname, "src/components"), then its internal import "./utils" is resolved as if the file were src/components/table.js importing src/components/utils.js.

const path = require("node:path");
const webpack = require("webpack");

module.exports = {
  plugins: [
    new webpack.experiments.schemes.VirtualUrlPlugin(
      {
        "src/components/button.js": {
          context: "auto",
          source() {
            return "import { trim } from './utils'; export const button = trim('button ');";
          },
        },
        "virtual/table.js": {
          context: path.join(__dirname, "src/components"),
          source() {
            return "import { trim } from './utils'; export const table = trim('table ');";
          },
        },
      },
      { context: "auto" },
    ),
  ],
};

Experimental JavaScript Parsing with oxc-parser

Webpack now includes an example demonstrating how to replace the default JavaScript parser with oxc-parser. This integration should be considered purely experimental and is not recommended for production use.

Instead, it is intended for development environments or benchmark branches, allowing the community to experiment with alternative parsing strategies in real projects. This helps evaluate potential improvements in parse time and build performance, as well as identify possible compatibility issues.

Example

The following configuration limits the custom parser to .js files:

"use strict";

const oxcParse = require("./internals/oxc-parse");

/** @type {import("webpack").Configuration} */
module.exports = {
  mode: "production",
  entry: "./src/index.js",
  module: {
    rules: [
      {
        // Apply the custom parser only to JavaScript files
        test: /\.js$/,
        parser: {
          parse: oxcParse,
        },
      },
    ],
  },
};

You can find the full example in the webpack repository: https://github.com/webpack/webpack/blob/main/examples/custom-javascript-parser/webpack.config.js

Ecosystem Updates

  • Webpack-cli has released a new major version, 7.0.0. The minimum supported Node.js version is now 20.9.0, and configuration files are loaded via dynamic import() by default, which enables native TypeScript configuration support through Node.js type stripping without needing external loaders. The --node-env argument has been replaced by --config-node-env, and the deprecated programmatic API has been removed. Additionally, configuration freezing is now allowed, graceful shutdown has been improved when file system cache is enabled, and general performance improvements have been made. Check the release for more information.
  • Webpack-dev-middleware has released a new major version, 8.0.0. The minimum supported Node.js version is now 20.9.0 and the minimum webpack version is 5.101.0. The getFilenameFromUrl function is now asynchronous, immutable asset caching (cacheImmutable) is enabled by default, and a new forwardError option allows forwarding errors to the next middleware. Support for plugin usage has also been added, and general performance improvements have been made. Check the release for more information.
  • Compression-webpack-plugin, html-minimizer-webpack-plugin, css-minimizer-webpack-plugin, image-minimizer-webpack-plugin, and other plugins have released new major versions to align their minimum supported Node.js version to 20.9.0, keeping consistency across the webpack ecosystem alongside the recent major releases of webpack-cli 7 and webpack-dev-middleware 8.

Other Improvements and Bug Fixes

Several bug fixes have been resolved since version 5.104. Check the changelog for more details.

Thanks

A big thank you to all our contributors and sponsors who made Webpack 5.106 possible. Your support, whether through code contributions, documentation, or financial sponsorship, helps keep Webpack evolving and improving for everyone.

Edit this page·

1 Contributor

bjohansebas