Let’s take a step back and look at the context.
To be included in the ECMAScript specification, a new feature passes through a specific process with five phases. So it could take some time before it is integrated into the specifications and then implemented in the browsers.
While we, developers, can’t wait to use new exciting features that improve our
life code, most browsers don’t support them yet, so we can’t just deliver the code as we wrote it.
Let’s take a look at the key points of interest regarding Babel:
babel.config.jsfile located at the project’s root.
@babel/preset-envto manage Babel plugins,
@babel/preset-typescriptfor TypeScript usage, or
@babel/preset-reactfor React applications.
@babel/preset-env, it will automatically decide which plugins and polyfills (thanks to
core-js) have to be applied when processing the code.
Below is a simple Babel configuration file that we will use as an example throughout this article:
core-js is an NPM package that contains all polyfills for every possible ECMAScript feature. It must be installed for Babel to work with this latter. We’ll learn more about its usage in the polyfills handling section below.
With the configuration from the previous section, Babel will output the code as follows:
As a result, arrow functions are transformed to the basic function syntax supported by every browser. Same thing for
.concat(), which is more widely supported by browsers than template literals. Finally,
const is transformed to
var, mainly for IE 11.
To do this transformation, Babel creates an AST (Abstract syntax tree), a tree representation of the code structure. Then it applies plugins that use this AST to transform and output the code. In this example, the plugin
@babel/plugin-transform-arrow-functions was used to transform arrow functions, but there are a lot of other Babel plugins to handle any transformation.
The good news is that it’s not necessary to know all of them to transform the code correctly, thanks to the
Indeed, this preset has a built-in list of plugins matching browser versions. So, according to the browser versions list provided, it knows precisely which plugins need to be applied.
According to the MDN:
A polyfill is a piece of code used to provide modern functionality on older browsers that do not natively support it.
Let’s take an example. Here is a code using
Array.prototype.find to find the first element matching a condition in an array:
This code works well on recent browsers, but when running on Internet Explorer 11, it throws an error
Object doesn't support property or method 'find'. Indeed, the
find() method for arrays doesn’t exist for this browser and won’t exist since this browser is not updated anymore.
The solution is to
drop IE 11 support provide a polyfill. In this case, it could be as simple as copying/pasting this polyfill from the MDN directly into the code to make it work.
But it is more complicated to do that for every feature used in a codebase. It’s easy to forget or duplicate too many of them in the code, while it’s complicated to test and monitor. This is where the NPM package
core-js full of ECMAScript polyfills, comes in.
As for the code transpiling, the preset
@babel/preset-env has a built-in list of
core-js polyfills names that match browsers versions. According to the targeting environments, it knows which polyfills to include. From this point on, you have three ways to do that using the
useBuiltIns option of
This option requires the
core-js module to be imported (and only once) at the entry point of the project. According to the standard level targeted, many import options are available:
Then Babel will parse the code, and when it finds the
core-js import, it will transform this one-line import into multiple imports of unit modules from
core-js. As a result, it’ll only import polyfills necessary for the targeting environments whether or not the features are used. Here’s what that looks like by importing
This option tells Babel to automatically write the polyfill imports related to a feature each time it encounters it.
Thus, this code:
will be transformed by Babel to this:
It’s important to understand that it’s no longer needed to write
core-js imports. Polyfills imports will automatically be added at every part of the code that needs one or many polyfills. It also means that if a modern feature is used multiple times at different places, it will result in multiple imports of the same polyfills. Indeed, it assumes that a bundler (like Webpack) will collect and deduplicate imports so that polyfills are only included once in the final output(s).
This is the most optimized and automatic way to include only the polyfills that are needed and remove them when they become unnecessary (whether they are not used anymore or the targeting environments list evolved to more recent ones).
This will tell Babel not to handle polyfills at all. Every polyfill from the different
core-js imports will be included without fine selections according to the targeted environments. It will be still possible to import
core-js manually. There won’t be any filtering but the selected modules imports:
Using this way, be sure never to import polyfill twice; otherwise, it will throw an error.
To do so, TypeScript comes with a code transpiler. This latter will transform the TypeScript code to an ECMAScript 3 code, so it could be wrongly thought that we don’t need to transpile with Babel anymore.
But there are some points that we need to highlight here:
.jsfiles and Typescript for
So a solution here would be to keep using Babel for both cases, thanks to a dedicated preset named
@babel/preset-typescript that allows Babel to transform TypeScript code correctly. And for the type-checking, we can still rely on the
tsc CLI provided by TypeScript.
At Getaround, we use a custom preset to share our configuration across all front-end apps. It is publicly available on our
drivy/frontend-configs Github repository, along with all of our other front-end configurations.
You can also find it on NPM:
Don’t hesitate to take a look and to use it!