BabelJs - Babel Polyfill
BabelJS - Polyfills for Modern APIs
Section titled “BabelJS - Polyfills for Modern APIs”While Babel excels at transpiling modern JavaScript syntax, it doesn’t inherently provide implementations for new APIs (like Promise, Map, Set, or new methods on built-ins like Array.prototype.includes). These are handled by polyfills. This chapter explains how to use polyfills with Babel to ensure your modern JavaScript code runs correctly in older environments.
Previously, @babel/polyfill was a common package for this. However, it has been deprecated. The modern approach involves using core-js (a comprehensive library of polyfills) and regenerator-runtime (for generators and async functions), often managed automatically by @babel/preset-env.
Common Features Requiring Polyfills
Section titled “Common Features Requiring Polyfills”Here’s a list of common JavaScript features that often need polyfills for older browser support:
- Promises (
Promise) - Collections (
Map,Set,WeakMap,WeakSet) - Symbols (
Symbol) - Iterators and Generators (require
regenerator-runtime) - Async/Await (require
regenerator-runtimeandPromisepolyfill) - Static methods on built-ins (e.g.,
Array.from,Object.assign,Object.entries) - Instance methods on built-ins (e.g.,
Array.prototype.includes,String.prototype.padStart)
Setting up Polyfills with @babel/preset-env
Section titled “Setting up Polyfills with @babel/preset-env”The recommended way to handle polyfills is by configuring @babel/preset-env. This preset can automatically include polyfills from core-js based on your code and target environments.
First, ensure you have the necessary packages installed. Create a new project:
mkdir babel-polyfill-examplecd babel-polyfill-examplenpm init -yInstall Babel and core-js:
npm install --save-dev @babel/core @babel/cli @babel/preset-envnpm install --save core-js regenerator-runtimeYour package.json devDependencies will look something like this (versions may vary):
{ "name": "babel-polyfill-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "babel src -d lib" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/cli": "^7.x.x", "@babel/core": "^7.x.x", "@babel/preset-env": "^7.x.x" }, "dependencies": { "core-js": "^3.x.x", "regenerator-runtime": "^0.x.x" }}Now, create a Babel configuration file, babel.config.json (or .babelrc.json), in your project root:
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3, "targets": { "browsers": ["> 0.25%", "not dead", "ie >= 11"] } } ] ]}Explanation of the configuration:
"useBuiltIns": "usage": This tells Babel to analyze your code and automatically add imports tocore-jsmodules for polyfills that are used and not supported by your target environments. This is very efficient as it only includes what’s needed."corejs": 3: Specifies that you are usingcore-jsversion 3."targets": Defines the environments you are targeting. Polyfills will be added based on the features missing in these environments. You can specify browser queries, Node versions, etc. (Seebrowserslistdocumentation for more: https://github.com/browserslist/browserslist)
Alternatively, with "useBuiltIns": "entry", you would manually add import "core-js/stable"; and import "regenerator-runtime/runtime"; at the beginning of your main entry file (e.g., src/index.js). Babel would then replace these with specific imports based on your targets.
Examples
Section titled “Examples”Let’s create a src directory and add some examples.
Example 1: Promises
Section titled “Example 1: Promises”Create src/promises-example.js:
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved after 1 second!"); }, 1000);});
myPromise.then(message => { console.log(message);});
async function fetchData() { await myPromise; console.log("Data fetched after promise resolved (using async/await).");}
fetchData();Compile this file using the build script in package.json:
npm run buildThis will create lib/promises-example.js. If you inspect the transpiled code, you’ll see Babel has transformed the async/await syntax and, due to useBuiltIns: "usage" and your targets, it would have injected import statements for core-js/modules/es.promise.js (or similar) and regenerator-runtime/runtime.js if Promises or async functions are not fully supported by your target (e.g., IE 11).
To run this in a browser, you’d typically use a bundler like Webpack or Parcel to resolve these imports and create a single bundle file. For a simple test, you could create an index.html:
<html><head> <title>Babel Polyfill Test</title></head><body> <h1>Testing Promises with Polyfills</h1> <!-- In a real app, you'd use a bundler. This direct script tag won't work if Babel added import statements for polyfills. For this example, assume you bundle lib/promises-example.js into a single bundle.js --> <!-- <script src="path/to/your/bundled_promises_example.js"></script> --> <p>Open your browser's console to see the output.</p> <p>If bundling 'lib/promises-example.js' and running it, the console would show:</p> <pre>Promise resolved after 1 second!Data fetched after promise resolved (using async/await). </pre></body></html>Note: The old method of directly including babel-polyfill/dist/polyfill.min.js is no longer the standard practice. Modern toolchains rely on modular polyfills managed by @babel/preset-env and bundlers.
Example 2: String Padding
Section titled “Example 2: String Padding”String.prototype.padStart() and String.prototype.padEnd() were introduced in ES2017.
Create src/string-padding-example.js:
const str = 'Babel';
console.log(str.padStart(10, '_')); // Expected: '_____Babel'console.log(str.padEnd(10, '*')); // Expected: 'Babel*****'After running npm run build, lib/string-padding-example.js will be generated. If your targets include browsers that don’t support string padding, Babel (via core-js) will ensure the necessary polyfills are included (or transformations made if possible). When bundled and run in such a browser, the console output would be:
_____BabelBabel*****Example 3: Map and Set
Section titled “Example 3: Map and Set”Map and Set are ES2015 features.
Create src/collections-example.js:
let myMap = new Map();myMap.set("name", "BabelJS");myMap.set("type", "Transpiler");console.log(myMap.get("name"));
let mySet = new Set();mySet.add(1);mySet.add(5);mySet.add(5); // duplicate, will be ignoredmySet.add(10);console.log(mySet.has(5));console.log("Set size:", mySet.size);After npm run build, if targeting older environments, the transpiled code in lib/collections-example.js, when bundled and run, would output:
BabelJStrueSet size: 3Example 4: Array Methods
Section titled “Example 4: Array Methods”Array.prototype.includes (ES2016) and Array.from (ES2015).
Create src/array-methods-example.js:
const numbers = [1, 2, 3, 4, 5];console.log("Includes 3:", numbers.includes(3)); // Expected: trueconsole.log("Includes 6:", numbers.includes(6)); // Expected: false
const newArray = Array.from("hello");console.log("Array from string:", newArray); // Expected: ['h', 'e', 'l', 'l', 'o']After npm run build, the transpiled code in lib/array-methods-example.js, when bundled and run, would output:
Includes 3: trueIncludes 6: falseArray from string: ["h", "e", "l", "l", "o"]Important Considerations
Section titled “Important Considerations”- Bundlers are Key: For browser applications, using a module bundler (like Webpack, Parcel, or Rollup) is essential. Bundlers resolve the
importstatements forcore-jsmodules andregenerator-runtimethat Babel might add, creating a single (or few) JavaScript file(s) for yourindex.html. - Targeting: Carefully define your
targetsin the Babel configuration. This ensures that polyfills are only included for browsers/environments that actually need them, optimizing your bundle size. core-jsVersion: Always specify thecorejsversion in your Babel config to ensure consistent behavior and avoid warnings.core-js@3is the modern standard.- Global Pollution:
core-jspolyfills can modify global objects (e.g.,Promise,Array.prototype). This is generally fine for applications but might be a concern for libraries. Libraries might prefer using@babel/plugin-transform-runtimeto avoid global pollution, which provides helpers that don’t modify globals.
By leveraging @babel/preset-env with core-js, you can confidently use modern JavaScript APIs and ensure your applications are robust and compatible across a wide range of environments. For detailed information on core-js and its capabilities, visit: https://github.com/zloirock/core-js