Building a Custom Webpack Plugin
Webpack is incredibly flexible — not because of its configuration file, but because everything inside Webpack is powered by plugins. Creating your own plugin lets you hook into Webpack’s lifecycle, inspect files, modify assets, generate content, or automate tasks during builds.
This guide walks you through creating a simple but fully functional Webpack plugin, with human-friendly explanations and modern examples.
1. Project Setup
If you don’t have a project yet:
mkdir custom-webpack-plugin
cd custom-webpack-plugin
npm init -y
npm install webpack --save-dev2. Create a Plugin File
Create a plugins directory:
mkdir plugins
touch plugins/CustomPlugin.js3. Writing Your First Webpack Plugin
A Webpack plugin is just a class with an apply() method. Webpack calls apply() automatically and passes the compiler object, which exposes the entire build lifecycle.
class CustomPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.done.tap('CustomPlugin', stats => {
console.log('Build is complete!');
console.log('Message:', this.options.message);
});
compiler.hooks.emit.tapAsync(
'CustomPlugin',
(compilation, callback) => {
console.log('Assets are being emitted...');
callback();
}
);
}
}
module.exports = CustomPlugin;4. Register the Plugin in Webpack
Create webpack.config.js:
touch webpack.config.jsConfigure Webpack:
const CustomPlugin = require('./plugins/CustomPlugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: __dirname + '/dist',
},
plugins: [
new CustomPlugin({
message: 'Hello from your custom plugin!'
}),
],
};5. Test Your Plugin
Create an entry file:
mkdir src
touch src/index.jsAdd some JS:
console.log('Hello from index.js');Run Webpack:
npx webpackYou should see:
Assets are being emitted...
Build is complete!Webpack Plugin Lifecycle Hooks
Webpack exposes dozens of hooks via compiler and compilation. Common ones:
| Hook | When it Runs |
|---|---|
emit | Before assets are written to dist/ |
compilation | During module compilation |
afterEmit | After writing assets |
done | Build has fully completed |
Hooks come in 3 types:
-
.tap()— synchronous -
.tapAsync()— async with callback -
.tapPromise()— async with Promise
Example: Adding a Custom File to the Output
class AddCustomFilePlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync(
'AddCustomFilePlugin',
(compilation, callback) => {
const content = 'This file was created by a Webpack plugin!';
compilation.assets['custom-file.txt'] = {
source: () => content,
size: () => content.length
};
callback();
}
);
}
}
module.exports = AddCustomFilePlugin;Register it:
const AddCustomFilePlugin = require('./plugins/AddCustomFilePlugin');
module.exports = {
mode: 'development',
plugins: [new AddCustomFilePlugin()],
};Running Webpack now generates:
dist/custom-file.txtConclusion
To create a Webpack plugin, you:
- Write a class with an
applymethod - Hook into Webpack’s lifecycle using compiler hooks
- Modify assets, metadata, or build behavior
- Register your plugin in
webpack.config.js
Once you understand how hooks work, Webpack becomes a fully programmable build engine — capable of automating anything you need.
If you’d like, I can also generate:
- a more advanced plugin, like asset minification
- a TypeScript version
- a Webpack 5 plugin template generator
- or a plugin that analyzes bundle size