Trim Your Bundle: Using Babel’s useBuiltIns to Auto‑Insert Only Needed Polyfills
Babel’s useBuiltIns option in @babel/preset‑env lets you auto‑inject polyfills only for the features you actually use. This reduces bundle size, speeds up load times, and keeps your codebase lean. Learn how to configure it, see a concrete example, and understand the trade‑offs.
09 Jul 2025, 09:01 UTC

The Problem: Bundle Bloat from Polyfills
Modern browsers support most ES2015+ features, but legacy browsers still need polyfills. The naive approach is to import a monolithic polyfill bundle (e.g., core-js/stable or polyfill.io) at the top of every entry file. That guarantees compatibility but adds hundreds of kilobytes to every build, even if the app never uses Promise or Array.from. The result is slower page loads, higher bandwidth costs, and a harder-to‑maintain codebase.
The Solution: Babel’s useBuiltIns
Starting with Babel 7.8.0, @babel/preset‑env can automatically insert the minimal polyfills required by the code you write. The useBuiltIns option tells Babel to piggy‑back on the core-js library and add imports only for the features that are actually used and that the target browsers don't natively support.
Three Modes Explained
- entry – Adds a single
import "core-js/stable";at the top of the bundle. You still get a full polyfill set, but you can control the entry point. - usage – Scans your source files and injects
import "core-js/modules/...";for every used feature. The polyfills are spread across the output files, so only the needed code is bundled. - usage with
corejs: 3– Same as usage but usescore-js@3instead of the oldercore-js@2. Core‑JS 3 is smaller and has a different module path.
Configuring @babel/preset‑env
Place the following in .babelrc (or babel.config.js if you prefer JS configuration):
{
"presets": [
["@babel/preset-env", {
"targets": {"browsers": ["last 2 versions", "ie 11"]},
"useBuiltIns": "usage",
"corejs": 3
}]
]
}
targetsmust match the browsers you intend to support; otherwise Babel may omit a necessary polyfill.corejsis required whenuseBuiltInsis notentry. Set it to2or3depending on the library version you install.- Make sure you have
core-js@3(orcore-js@2) installed as a dev dependency.
Selecting the Right Polyfill Library
Core‑JS 3 is the recommended library for new projects. It is modular, tree‑shakable, and smaller than its predecessor. Install it with:
npm install --save-dev core-js@3
If you are maintaining an older codebase that already uses core-js@2, you can keep it, but consider upgrading to benefit from the smaller footprint and better module support.
A Worked Example
Suppose you have the following source file src/app.js:
const arr = [1, 2, 3];
const newArr = Array.from(arr, x => x * 2);
const greet = () => {
console.log("Hello, world!");
};
greet();
Running Babel with the configuration above will produce an output file lib/app.js that contains imports only for Array.from and Promise if you later use it. The core-js/modules/es.array.from.js module will be injected automatically. No unnecessary polyfills for Object.assign or String.prototype.includes will appear because those functions are not used.
To verify:
- Run
npx babel src --out-dir lib(ensure you have Babel CLI installed). - Open
lib/app.jsand look forimport "core-js/modules/es.array.from.js";at the top. - Check that the bundle size is smaller than if you had imported
core-js/stablemanually.
Trade‑Offs & Limitations
- Build Time – Babel must analyze every file to determine which polyfills to insert, which can add a few seconds to the build, especially in large projects.
- Runtime Errors – If
targetsis mis‑configured (e.g., you forget to includeie 11), Babel will think the browser supports a feature and skip the polyfill. The application will then crash at runtime. - Conflicts with Other Polyfill Strategies – Using
useBuiltInstogether with@babel/plugin-transform-runtimeor external services likepolyfill.iocan lead to duplicate polyfills. Pick one strategy and stick with it. - Feature Support – Some polyfills are not available in
core-js(e.g., certain proposals). In those cases you may need to import a custom polyfill manually.
Actionable Steps
Checklist
- Upgrade to Babel 7.8.0 or later.
- Install
core-js@3as a dev dependency. - Set
targetsto match your supported browsers. - Choose
useBuiltIns: "usage"(or"entry"if you prefer a single import). - Verify the output contains only the necessary imports.
Verify Your Bundle
After building, run a quick size comparison:
npx size-limit src/app.js lib/app.js
This will show the difference in file size with and without useBuiltIns. Additionally, open the browser console and check for missing polyfill errors when loading the page.
Next Steps
- Integrate the Babel config into your bundler (Webpack, Rollup, Vite). Most bundlers respect the Babel config automatically.
- Consider adding a
browserslistfield topackage.jsonso that both Babel and your bundler use the same target list. - Monitor bundle size growth over time; if new features are added, re-run the build to ensure polyfills remain minimal.
By using Babel’s useBuiltIns wisely, you keep your JavaScript bundles lean, reduce load times, and maintain compatibility with older browsers without manual polyfill maintenance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.