The end of @babel/polyfill: The more efficient alternative?

Karen Kua
3 min readJun 24, 2020

The @babel/polyfill package allows you to emulate an ES6+ environment. Effectively, it ensures your ES6+ code is backwards compatible with older browsers and environments. It sounds like a golden fixture in modern JavaScript, right? Well, as of Babel 7.4.0, this powerful package had been deprecated. Composed of two dependent packages, core-js and regenerator-runtime, Babel now recommends installing them as dependencies and importing them at the top level of your application (think of index.js in a React app, for example).

  • core-js: a collection of polyfills to support stable and experimental features of the latest ECMAScript
  • regenerator-runtime: a package to support generator functions and async/await syntax

However, there was an alarming caveat with this recommended solution: a significantly increased bundle size.

Since you’re globally importing all of the polyfills and regenerator-runtime, an increased bundle size is a given. Luckily, this isn’t the only way to inject polyfills and support generator and async functions. You can minimize your bundle size by including the packages as part of your bundling process (at compile time) and importing their modules only when they’re needed. Please see the steps below:

Note: I am using Node 12.14.1 and Webpack as my bundler of choice.

  1. Install core-js, @babel/core, babel-loader, and @babel/preset-env as devDependencies.

yarn add core-js @babel/core babel-loader @babel/preset-env --dev

You may be wondering why you're not installing regenerator-runtime. Well, if you study your lock file, you’ll notice that @babel/plugin-transform-regenerator and @babel/runtime are already included as dependencies to your previously installed packages. They’ll add compatibility for generator and async functions.

2. Next, to ensure the polyfills and Babel features are loaded at compile time, you will need to add babel-loader to your Webpack configuration:

module: {  rules: [    {      test: /\.js$/,      loader: 'babel-loader',      exclude: /node_modules/    }
]
}

3. Finally, in your .babelrc file, add @babel/preset-env as a preset with the following configuration:

["@babel/preset-env", {   "useBuiltIns": "usage",   "corejs": 3.6}]
  • corejs: Here, you’re setting the version of core-js to target for polyfills. Setting the minor version ensures Babel will be able to support the latest, stable core-js features.
  • useBuiltIns: This option allows you to configure how Babel handles polyfills. It can be set to entry, usage, or false.

— The entry option transforms direct imports of core-js to imports of the modules within core-js that are required by a target environment. If you were to use this option, you would need to add these import statements to the entry point of your app:

import 'core-js';import 'regenerator-runtime/runtime';

— By contrast, the usage option automatically imports polyfills when they’re needed at the top level of each file for features that aren’t supported in the environment. You may be wondering if the polyfills will be excessively loaded when using the usage option. Thankfully, the bundler only loads the same polyfill once.

Therefore, for this tutorial, employ the usage option since you’re not using direct import statements for core-js and you’ll be able to take advantage of how polyfills will be automatically imported on an as needed basis for each file.

--

--

Karen Kua

Software developer specializing in React and Amazon Web Services.