The @angular-builders/custom-webpack package lets you merge your own webpack configuration into the Angular CLI build pipeline without ejecting, giving you full control over bundling while keeping the CLI's update path intact. It is the community-standard answer to the recurring question, "how do I add a webpack loader or plugin to an Angular app?", now that the old ng eject command is gone. This guide explains what the builder does, how to wire it up, and the security considerations that come with running arbitrary build-time code.
The problem it solves
Angular CLI hides webpack behind its own builder abstraction on purpose. That is great until you need something the CLI does not expose: a custom loader for an unusual asset type, a plugin like a bundle analyzer, a define-plugin variable, or a module resolution tweak. Historically you could ng eject to get the raw webpack config, but that command was removed because it froze you out of future CLI improvements.
The angular-builders/custom-webpack project fills that gap. Instead of replacing the CLI's webpack config wholesale, it merges your partial config into the one the CLI generates. You keep every benefit of the managed pipeline and layer your changes on top.
How to set it up
Install the package, then point your build target at the new builder in angular.json.
npm install --save-dev @angular-builders/custom-webpack
In angular.json, change the build target's builder and add a customWebpackConfig option:
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./custom-webpack.config.js",
"mergeRules": { "module": { "rules": "prepend" } }
}
}
}
Then write the partial config. It can export a plain object, or a function that receives the CLI's config and returns a modified one:
// custom-webpack.config.js
module.exports = (config, options) => {
config.plugins.push(new SomePlugin());
return config;
};
The builder merges your delta with the default. The replaceDuplicatePlugins option controls whether your plugins replace the CLI's matching ones or add alongside them, and mergeRules gives you fine control over how module rules combine. The package ships parallel builders for dev-server, server, karma, and extract-i18n, and it supports writing the config in TypeScript, executed through ts-node, plus an indexTransform hook for rewriting index.html.
The security angle nobody mentions
Here is the part that matters for a security-conscious team: a custom webpack config is arbitrary code that runs at build time with the full privileges of your build process. That is not a flaw in @angular-builders/custom-webpack; it is the nature of what it enables. But it changes your threat model in two ways.
Your build now executes code from more sources. Every loader and plugin you add is another dependency that runs during the build, often with filesystem and network access. A compromised build-time dependency can read environment variables (including secrets injected into CI), tamper with the emitted bundle, or exfiltrate source. This is the classic build-tooling supply chain risk, and adding a custom webpack layer widens the set of packages that participate in it.
The config file itself is a sensitive artifact. Because it can require any module and run any code, a malicious pull request that edits custom-webpack.config.js is effectively a request to run code on your build server. Review changes to build configuration with the same seriousness you would apply to CI pipeline changes, not the casual glance a config file usually gets.
Practical mitigations:
- Pin
@angular-builders/custom-webpackand every loader and plugin to exact versions, and scan them. An SCA tool will flag a known-vulnerable webpack plugin in your devDependencies, which is exactly where build-time risk hides and where people forget to look because "it's only a dev dependency." - Keep the custom config minimal. The more plugins you pile in, the larger your build-time attack surface.
- Run builds in an ephemeral, least-privilege CI environment so a compromised build-time dependency cannot reach long-lived credentials.
- Require review on any change to
angular.jsonbuild targets or the webpack config file.
When you actually need it, and when you do not
Reach for this builder when you have a concrete requirement the CLI cannot meet: a specialized loader, a bundle-analysis plugin for a one-off investigation, or a define variable. Do not adopt it "just in case." Every custom config you add is something you now own across Angular major upgrades, and while the merge approach survives CLI changes far better than ejecting did, a config that reaches deep into webpack internals can still break when the CLI restructures its own config.
If your only need is environment variables or simple asset copying, the CLI already handles those, and you should exhaust the built-in options first. Our academy covers evaluating build-tooling dependencies for supply chain risk, which is the right lens for any addition to your build pipeline.
FAQ
What does @angular-builders/custom-webpack do?
It lets you merge a custom webpack configuration into the Angular CLI build without ejecting. Your partial config is combined with the CLI's generated config, so you gain control over loaders, plugins, and bundling while keeping CLI updates.
Is it an official Angular package?
No. It is a widely used community project (the just-jeb/angular-builders repository), not maintained by the Angular team. It is popular and mature, but treat it as a third-party dependency and scan it like one.
Does using custom webpack config introduce security risk?
It runs arbitrary code at build time with your build process's privileges. The package itself is not the risk; the loaders, plugins, and config you add are. Pin and scan them, review config changes carefully, and build in a least-privilege environment.
Do I still need it, or can Angular CLI do everything now?
For common needs like environment variables and asset copying, the CLI suffices. You need the custom-webpack builder only for capabilities the CLI does not expose, such as custom loaders or specific webpack plugins.