Configure @babel/preset-env with browserslist and core-js 3 usage mode
Configure Babel 7 to down‑compile syntax only for browsers that need it and inject core‑js 3 polyfills only where used, with steps to verify and recover from common misconfigurations.
21 Sept 2026, 05:56 UTC

Desired outcome
You want a build where Babel down‑compiles only the JavaScript syntax that older browsers actually need, and where polyfills are added only for the features you use. This keeps the bundle size predictable and avoids accidental global pollution.
Prerequisites
- A Node.js project (npm or yarn) with
@babel/coreand@babel/preset-envinstalled as dev dependencies. - At least one source file that uses modern syntax (e.g., optional chaining
?.,async/await, or class fields). - Basic familiarity with running npm scripts from the project root; no elevated permissions are required.
Procedure
1. Declare browser targets
Create a .browserslistrc in the project root (or add a "browserslist" field to package.json). Example content:
# .browserslistrc
> 0.5%
not dead
This list is read automatically by @babel/preset-env and shared with tools like Autoprefixer, keeping CSS and JS targets in sync.
2. Choose a polyfill strategy
Install core-js version 3 as a dependency (not a dev dependency) because the polyfills are needed at runtime:
npm install core-js@3
Then configure @babel/preset-env to inject polyfills only where they are used:
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": { "version": 3 }
}]
]
}
Save this as babel.config.json (project‑wide) or .babelrc (file‑relative). Using babel.config.json avoids merge surprises in monorepos.
3. Add a build script
In package.json add a script that runs the Babel CLI:
"scripts": {
"build": "babel src --out-dir lib --source-maps"
}
Run the script from the project root:
npm run build
The command reads the effective Babel config, applies syntax transforms per the browserslist, and inserts import 'core-js/...' statements only where the static analysis detects usage.
Expected checks
Verify resolved targets
Run:
npx browserslist
The output should match the browsers you listed (e.g., Chrome 115, Firefox 115, Safari 16). If the list is empty, preset‑env will target all browsers, leading to larger output.
Inspect compiled output
After building, open a generated file in lib/ that originally used async/await. You should see:
- No
regeneratorRuntimereferences if the target browsers support async functions natively. - Or, if the target lacks native async support, a helper import like
require("@babel/runtime/helpers/asyncToGenerator")(when using@babel/plugin-transform-runtime) or core‑js polyfills. - Core‑js imports appear only near the actual usage (e.g.,
import "core-js/modules/es.promise.js">where a Promise is used).
To see which plugins were active, set an environment variable before building:
BABEL_SHOW_CONFIG_FOR=src/index.js npm run build
This prints the merged config and the list of plugins preset‑env applied for the given file.
Recovery options
RegeneratorRuntime is not defined
This error means async/await was down‑compiled but the runtime helper or polyfill is missing. Fixes:
- Ensure
useBuiltInsis set to"usage"or"entry"with a matchingcorejsversion. - If you prefer not to pollute globals, add
@babel/plugin-transform-runtimeto the presets/plugins and remove core‑js injection.
Core‑js version mismatch
If you installed core-js@2 but set "corejs": { "version": 3 } (or vice‑versa), Babel will either duplicate polyfills or miss them. Verify with:
npm ls core-js
Then adjust the corejs option to match the installed major version.
Unexpectedly large ES5 bundle
A bloated output usually indicates an empty or missing browserslist, causing preset‑env to target every browser. Check:
- Run
npx browserslist– it should return a non‑empty list. - Confirm that
.browserslistrcor the"browserslist"field is present and not commented out.
If you intentionally want to support a very wide range, consider splitting the build into modern and legacy bundles instead of targeting all browsers with a single preset.
Rollback
The steps above only add configuration files and adjust npm scripts; they do not alter source code. To revert, delete .browserslistrc (or remove the browserslist field), reset babel.config.json to a minimal preset, and remove the core-js dependency if it is no longer needed. Re‑run the build to confirm the previous behavior.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.