Selective Polyfilling with Core‑JS 3.x: Keep Your Bundle Lean
Learn how to use Core‑JS 3.x to polyfill only the features you need. This guide shows the difference between <code>pure</code> and <code>stable</code> imports, walks through a Promise polyfill example, and explains how to keep your bundle lean.
01 Aug 2026, 06:19 UTC

Why Do We Need Selective Polyfills?
Modern browsers ship many ECMAScript features, but legacy environments—like Internet Explorer 11 or Node 10—still miss useful APIs. A polyfill fills that gap by adding the missing feature at runtime. The problem arises when you import the entire core-js library: it adds dozens of unused polyfills and bloats the bundle. For a production site that serves mobile users, a 100 KB increase can be costly.
Core‑JS 3.x: Modular Architecture
Core‑JS 3.x splits polyfills into individual modules. Instead of a monolithic core-js package, you import only what you need:
// Import the Promise constructor for environments that lack it
import 'core-js/es/promise';
Each module lives under core-js/es/… or core-js/stable/…. The es path contains the latest ECMAScript standard, while stable includes features that are safe to use across all browsers that support the feature’s specification.
Pure vs. Stable: Global Side‑Effects vs. Tree‑Shaking
Core‑JS offers two entry points:
- core-js/stable – injects polyfills into global objects (e.g.,
Array.prototype). It’s convenient for quick experiments but makes tree‑shaking impossible because the bundler can’t know which polyfills are actually used. - core-js/pure – exposes polyfills as pure functions without touching globals. When you import a module from
core-js/pure, it returns the constructor or prototype method, and the bundler can drop unused code. This is the preferred path for production builds that rely on a module bundler like Webpack or Rollup.
Mixing the two strategies in the same codebase can lead to duplicate polyfills or inconsistent behavior, so pick one and stick with it.
Practical Example: Adding Promise Support to an Old Browser
Suppose you need Promise in Internet Explorer 11. You can add the polyfill without pulling in the rest of the library:
// app.js
import 'core-js/es/promise'; // or import { Promise } from 'core-js/pure/es/promise';
// Now you can use Promise normally
new Promise((resolve) => resolve('Hello')).then(console.log);
Verification steps:
- Check the installed version:
npm list core-jsshould show a 3.x release. - Run the script in an environment that lacks native Promise support (e.g., Node 10). The console should output
Hello. - Use a bundler analysis tool (e.g.,
webpack-bundle-analyzer) to confirm the bundle contains only the Promise polyfill, not the entire library.
Trade‑off: Bundle Size vs. Convenience
Importing the full core-js bundle guarantees that any missing feature is available, but it can increase your payload by 50–100 KB per polyfill. Selective imports keep the bundle small but require you to maintain a list of features you actually need. Team consistency is key: document the chosen strategy and enforce it via linting or a build script.
Actionable Checklist
- Decide on a strategy:
purefor tree‑shaking orstablefor quick prototypes. - Use
core-js/compatto map target browsers to required polyfills. - Import only the modules you need, e.g.,
import 'core-js/es/array/flat-map'; - Run a bundle analysis after each change to verify size impact.
- Avoid mixing
core-js/stableandcore-js/pureimports in the same project.
By following these steps you can support legacy environments without sacrificing performance.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.