Webpack 5.106
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
- In development mode,
validatedefaults totrue. - In production mode,
validatealso defaults totrue. However, ifexperiments.futureDefaultsis enabled, it defaults tofalse.
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

