Optimizing Bundle Size with @babel/preset-env and useBuiltIns
Learn how to use @babel/preset-env with 'useBuiltIns: usage' to automatically inject only the necessary polyfills, reducing JavaScript bundle size without breaking older browser support.
14 Aug 2026, 23:48 UTC

The Problem: Bloated Bundles from Over-Polyfilling
Many developers include a global polyfill (like importing the entire core-js library) to ensure modern JavaScript features work in older browsers. This approach forces every user to download polyfills for features your application may not even use, significantly increasing the initial bundle size and slowing down page load times.
The solution is to use @babel/preset-env with the useBuiltIns: 'usage' configuration. This tells Babel to analyze your code and inject only the specific polyfills required by the features you actually wrote, targeted specifically to the browsers you support.
How Automated Polyfilling Works
Babel operates by converting your source code into an Abstract Syntax Tree (AST)—a tree-like representation of the code's structure. When @babel/preset-env is active, Babel checks your targets (the list of browsers you support) against a database of feature support. If you use a feature (like Array.prototype.includes) that is missing in one of your target browsers, Babel adds an import for that specific polyfill from core-js at the top of the file.
Configuration Example
To implement this, you need @babel/core, @babel/preset-env, and core-js installed in your project. Create a babel.config.json in your root directory:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "> 0.25%, not dead",
"useBuiltIns": "usage",
"corejs": "3.38"
}
]
]
}
Key Configuration Parameters
- targets: Defines the environment.
"> 0.25%, not dead"uses Browserslist syntax to target browsers with more than 0.25% global usage that are still receiving official updates. - useBuiltIns: 'usage': The most efficient setting. It scans each file and adds
import "core-js/modules/es.array.includes.js"only if.includes()is detected in that file. - corejs: Specifies the version of
core-jsto use. Omitting this or using an outdated version can result in Babel attempting to import modules that do not exist in your installed version of the library.
Comparing Polyfill Strategies
| Setting | Behavior | Bundle Impact | Risk |
|---|---|---|---|
false (Default) |
No polyfills added automatically. | Smallest | Runtime crashes in older browsers. |
'entry' |
Replaces a single global import with all polyfills needed for targets. | Medium/Large | Includes polyfills you aren't using. |
'usage' |
Adds specific imports based on code analysis. | Optimized | Potential for missing polyfills in complex dynamic code. |
Limitations and Common Mistakes
While useBuiltIns: 'usage' is powerful, it is not a silver bullet. It relies on static analysis, meaning Babel reads your code without running it. If you use a feature dynamically—such as calling a method via a variable name—Babel may not detect the need for a polyfill.
Common Pitfalls
- Missing core-js installation: Babel does not install
core-jsfor you. If you configureuseBuiltInsbut forget tonpm install core-js, your build tool (like Webpack or Vite) will throw a "Module not found" error. - Over-transpilation: Setting targets too broadly (e.g., including IE 11 when your users are on modern Chrome) forces Babel to convert arrow functions to standard functions and add numerous polyfills, increasing the final file size.
- Version Mismatch: Specifying
"corejs": "3"is often too vague. Specifying the minor version (e.g.,"3.38") ensures Babel uses the most recent API mappings.
Verification and Diagnostics
To verify that your configuration is working and to see exactly which plugins are being applied, run the Babel CLI with the debug flag from your terminal:
# Run in project root with npm/npx permissions
npx babel src --out-dir dist --debug
Expected Result: The terminal will output a detailed list of the target environments and the specific plugins (e.g., transform-arrow-functions) and polyfills being injected. If you see core-js imports in your compiled dist files that correspond to the features you used, the configuration is successful.
Rollback: To revert to default behavior and remove automated polyfilling, remove the useBuiltIns and corejs keys from your babel.config.json and run your build command again.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.