This article covers Babel 6, the current stable release

Introduction to Babel

Babel is an awesome tool, and it’s been around for quite some time, but nowadays almost every JavaScript developer relies on it, and this will continue going on, because Babel is now indispensable and has solved a big problem for everyone.

Which problem?

The problem that every Web Developer has surely had: a feature of JavaScript is available in the latest release of a browser, but not in the older versions. Or maybe Chrome or Firefox implement it, but Safari iOS and Edge do not.

For example, ES2015 introduced the arrow function:

[1, 2, 3].map((n) => n + 1)

Which is now supported by all modern browsers. IE11 does not support it, nor Opera Mini (How do I know? By checking the ES6 Compatibility Table).

So how should you deal with this problem? Should you move on and leave the customers with older/incompatible browsers behind, or should you write older JavaScript code to make all your users happy?

Enter Babel. Babel is a compiler: it takes code written in one standard, and it transpiles it to code written into another standard.

You can configure Babel to transpile modern ES2017 JavaScript into JavaScript ES5 syntax:

[1, 2, 3].map(function(n) {
  return n + 1
})

This must happen at build time, so you must setup a workflow that handles this for you. Webpack is a common solution.

(P.S. if all this ES thing sounds confusing to you, see more about ES versions in the ECMAScript guide)

Installing Babel

Babel is easily installed using npm or Yarn:

npm install --global babel-cli

or

yarn global add babel-cli

This will make the global babel command available in the command line:

Babel CLI

Now inside your project install the babel-core and babel-loader packages, by running:

npm install babel-core babel-loader --save-dev

or

yarn add --dev babel-core babel-loader

By default Babel does not provide anything, it’s just a blank box that you can fill with plugins to solve your specific needs.

An example Babel configuration

Babel out of the box does not do anything useful, you need to configure it.

To solve the problem we talked about in the introduction (using arrow functions in every browser), we can run

npm install --save-dev \
    babel-plugin-transform-es2015-arrow-functions

or (Yarn)

yarn add --dev \
     babel-plugin-transform-es2015-arrow-functions

to download the package in the node_modules folder of our app, then we need to add

{
  "plugins": ["transform-es2015-arrow-functions"]
}

to the .babelrc file present in the application root folder. If you don’t have that file already, you just create a blank file, and put that content into it.

TIP: If you have never seen a dot file (a file starting with a dot) it might be odd at first because that file might not appear in your file manager, as it’s a hidden file.

Now if we have a script.js file with this content:

var a = () => {};
var a = (b) => b;

const double = [1,2,3].map((num) => num * 2);
console.log(double); // [2,4,6]

var bob = {
  _name: "Bob",
  _friends: ["Sally", "Tom"],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};
console.log(bob.printFriends());

running babel script.js will output the following code:

var a = function () {};var a = function (b) {
  return b;
};

const double = [1, 2, 3].map(function (num) {
  return num * 2;
});console.log(double); // [2,4,6]

var bob = {
  _name: "Bob",
  _friends: ["Sally", "Tom"],
  printFriends() {
    var _this = this;

    this._friends.forEach(function (f) {
      return console.log(_this._name + " knows " + f);
    });
  }
};
console.log(bob.printFriends());

As you can see arrow functions have all been converted to JavaScript ES5 functions.

Babel presets

We just saw in the previous article how Babel can be configured to transpile specific JavaScript features.

You can add much more plugins, but you can’t add to the configuration features one by one, it’s not practical.

This is why Babel offers presets.

The most popular presets are es2015, env and react.

es2015 preset

This preset provides all the ES2015 features. You install it by running

npm install --save-dev babel-preset-es2015

or

yarn add --dev babel-preset-es2015

and by adding

{
  "presets": ["es2015"]
}

to your .babelrc file.

env preset

The env preset is very nice: you tell it which environments you want to support, and it does everything for you, supporting all modern JavaScript features.

E.g. “support the last 2 versions of every browser, but for Safari let’s support all versions since Safari 7`

{
  "presets": [
    ["env", {
      "targets": {
        "browsers": ["last 2 versions", "safari >= 7"]
      }
    }]
  ]
}

or “I don’t need browsers support, just let me work with Node.js 6.10”

{
  "presets": [
    ["env", {
      "targets": {
        "node": "6.10"
      }
    }]
  ]
}

react preset

The react preset is very convenient when writing React apps, by adding preset-flow, syntax-jsx, transform-react-jsx, transform-react-display-name.

By including it, you are all ready to go developing React apps, with JSX transforms and Flow support.

More info on presets

https://babeljs.io/docs/plugins/

Using Babel with Webpack

If you want to run modern JavaScript in the browser, Babel on its own is not enough, you also need to bundle the code. Webpack is the perfect tool for this.

TIP: read the Webpack guide if you’re not familiar with Webpack

Modern JS needs two different stages: a compile stage, and a runtime stage. This is because some ES6+ features need a polyfill or a runtime helper.

To install the Babel polyfill runtime functionality, run

npm install --save babel-polyfill \
                   babel-runtime \
                   babel-plugin-transform-runtime

or

yarn add babel-polyfill \
         babel-runtime \
         babel-plugin-transform-runtime

Now in your webpack.config.js file add:

entry: [
  'babel-polyfill',
  // your app scripts should be here
],

module: {
  loaders: [
    // Babel loader compiles ES2015 into ES5 for
    // complete cross-browser support
    {
      loader: 'babel-loader',
      test: /\.js$/,
      // only include files present in the `src` subdirectory
      include: [path.resolve(__dirname, "src")],
      // exclude node_modules, equivalent to the above line
      exclude: /node_modules/,
      query: {
        // Use the default ES2015 preset
        // to include all ES2015 features
        presets: ['es2015'],
        plugins: ['transform-runtime']
      }
    }
  ]
}

By keeping the presets and plugins information inside the webpack.config.js file, we can avoid having a .babelrc file.