The @babel/plugin-proposal-class-properties package is deprecated rather than vulnerable, and the security concern is supply chain hygiene: you are depending on an unmaintained plugin when a maintained, drop-in replacement exists. Class fields reached the ECMAScript standard, so Babel folded the transform into a stable plugin and stopped maintaining the old "proposal" name. Continuing to depend on the deprecated package is the kind of low-grade debt that quietly accumulates across a monorepo.
This guide explains why the plugin was renamed, why deprecated dependencies are a real (if unglamorous) security topic, and how to migrate in a few minutes.
Why the plugin was renamed
Babel's plugin names historically tracked the TC39 proposal stage of the feature they implemented. Class properties (also called class fields) were a Stage proposal, hence plugin-proposal-class-properties. Once the feature was accepted into the ECMAScript standard, the "proposal" framing no longer made sense.
Babel 7.14 introduced the renamed, stable plugin @babel/plugin-transform-class-properties. The old @babel/plugin-proposal-class-properties still installs, but its npm page now carries a deprecation notice pointing users to the transform plugin. As the npm listing states, the proposal was merged into the standard and users should switch to the transform plugin. There is no new development on the old name.
There is a second, easy-to-miss fact: if you use @babel/preset-env on a modern version, class fields are already handled for you based on your targets. Many projects that explicitly list a class-properties plugin do not need it at all anymore.
Why a deprecated dependency is a security topic
"It is only deprecated, not vulnerable" is a common shrug, and it misreads the risk. Deprecated packages matter for several concrete reasons.
A deprecated package receives no security fixes. The old name will not get a patch if a vulnerability is ever found in shared Babel helper code that it pulls in. You are pinned to a frozen artifact.
Deprecated packages are attractive squatting and confusion targets. Unmaintained namespaces and their look-alikes are where typosquatting and dependency-confusion attacks concentrate, because fewer eyes are watching. Reducing your count of stale, low-attention dependencies shrinks that surface.
Deprecation notices are noise that hides signal. When npm install prints a wall of deprecation warnings, teams learn to ignore them, and the one warning that actually matters scrolls past. Clearing avoidable deprecations keeps that channel meaningful.
None of this requires a CVE to be worth acting on. It is basic dependency hygiene, and dependency hygiene is where a large share of supply chain incidents begin.
How to migrate
The swap is mechanical. First, install the replacement and remove the old package:
npm install --save-dev @babel/plugin-transform-class-properties
npm uninstall @babel/plugin-proposal-class-properties
Then update your Babel config. In babel.config.json or .babelrc:
{
"plugins": ["@babel/plugin-transform-class-properties"]
}
The plugin options are the same, including loose mode, so a config like ["@babel/plugin-transform-class-properties", { "loose": true }] migrates without behavioral change. If you also declare @babel/plugin-proposal-private-methods or @babel/plugin-proposal-private-property-in-object, note those were renamed the same way to their plugin-transform-* equivalents, and mixing loose settings between them will trigger a Babel warning. Keep the loose value consistent across the three.
After the swap, run your build and test suite. Because the transform is functionally identical, a green build is the expected result.
Consider dropping the plugin entirely
Before you even migrate, check whether you need the plugin. If your @babel/preset-env is current and your browserslist/targets include only environments that support native class fields, preset-env will not transform them and you can delete the standalone plugin. Fewer explicit plugins means fewer things to keep patched. Test after removal, since a very old target still needs the transform.
Fitting this into a broader cleanup
One deprecated plugin is trivial. The value comes from doing this systematically across every service. A dependency scan that flags deprecated and unmaintained packages, not only ones with published CVEs, turns this from a chance discovery into a tracked backlog. Automated software composition analysis can surface deprecated Babel plugins across a monorepo in one pass, and the Safeguard academy has more on why maintenance status belongs in your risk model alongside known vulnerabilities.
FAQ
Is @babel/plugin-proposal-class-properties a security vulnerability?
No. It has no known CVE. It is deprecated because class fields became part of the ECMAScript standard and Babel moved the transform to a stable plugin. The security concern is depending on an unmaintained package that will receive no future fixes, not an active exploit.
What replaces @babel/plugin-proposal-class-properties?
@babel/plugin-transform-class-properties, introduced in Babel 7.14. It is a drop-in replacement with the same options, including loose mode. Install it, uninstall the old package, and update the plugin name in your Babel config.
Do I still need a class-properties plugin at all?
Often not. If you use a current @babel/preset-env and your target environments support native class fields, preset-env handles them and you can remove the standalone plugin entirely. Verify with your test suite, since very old targets still require the transform.
Why bother migrating if it still works?
Deprecated packages get no security patches, attract typosquatting attention, and add deprecation noise that trains teams to ignore warnings. The migration is a one-line config change, so the cost is near zero and the hygiene benefit is real.