This is the original source reformatted in a single-page book edition (using the Manuscripts format).
See the source repo for how the book gets auto-built with "plain" Jekyll - of course - and hosted on GitHub Pages.
Onwards.
Babel is a generic multi-purpose compiler for JavaScript. Using Babel you can use (and create) the next generation of JavaScript, as well as the next generation of JavaScript tooling.
JavaScript as a language is constantly evolving, with new specs and proposals coming out with new features all the time. Using Babel will allow you to use many of these features years before they are available everywhere.
Babel does this by compiling down JavaScript code written with the latest standards into a version that will work everywhere today. This process is known as source-to-source compiling, also known as transpiling.
For example, Babel could transform the new ES2015 arrow function syntax from this:
const square = n => n * n;
Into the following:
const square = function square(n) {
return n * n;
};
However, Babel can do much more than this as Babel has support for syntax extensions such as the JSX syntax for React and Flow syntax support for static type checking.
Further than that, everything in Babel is simply a plugin and anyone can go out and create their own plugins using the full power of Babel to do whatever they want.
Even further than that, Babel is broken down into a number of core modules that anyone can use to build the next generation of JavaScript tooling.
Many people do too, the ecosystem that has sprung up around Babel is massive and very diverse. Throughout this handbook I’ll be covering both how built-in Babel tools work as well as some useful things from around the community.
For future updates, follow @thejameskyle on Twitter.
Since the JavaScript community has no single build tool, framework, platform, etc., Babel has official integrations for all of the major tooling. Everything from Gulp to Browserify, from Ember to Meteor, no matter what your setup looks like there is probably an official integration.
For the purposes of this handbook, we’re just going to cover the built-in ways of setting up Babel, but you can also visit the interactive setup page for all of the integrations.
Note: This guide is going to refer to command line tools like
node
andnpm
. Before continuing any further you should be comfortable with these tools.
babel-cli
Babel’s CLI is a simple way to compile files with Babel from the command line.
Let’s first install it globally to learn the basics.
$ npm install --global babel-cli
We can compile our first file like so:
$ babel my-file.js
This will dump the compiled output directly into your terminal. To write it to
a file we’ll specify an --out-file
or -o
.
$ babel example.js --out-file compiled.js
# or
$ babel example.js -o compiled.js
If we want to compile a whole directory into a new directory we can do so using
--out-dir
or -d
.
$ babel src --out-dir lib
# or
$ babel src -d lib
While you can install Babel CLI globally on your machine, it’s much better to install it locally project by project.
There are two primary reasons for this.
We can install Babel CLI locally by running:
$ npm install --save-dev babel-cli
Note: Since it’s generally a bad idea to run Babel globally you may want to uninstall the global copy by running:
$ npm uninstall --global babel-cli
After that finishes installing, your package.json
file should look like this:
{
"name": "my-project",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
Now instead of running Babel directly from the command line we’re going to put our commands in npm scripts which will use our local version.
Simply add a "scripts"
field to your package.json
and put the babel command
inside there as build
.
{
"name": "my-project",
"version": "1.0.0",
+ "scripts": {
+ "build": "babel src -d lib"
+ },
"devDependencies": {
"babel-cli": "^6.0.0"
}
}
Now from our terminal we can run:
npm run build
This will run Babel the same way as before, only now we are using a local copy.
babel-register
The next most common method of running Babel is through babel-register
. This
option will allow you to run Babel just by requiring files, which may integrate
with your setup better.
Note that this is not meant for production use. It’s considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First let’s create an index.js
file in our project.
console.log("Hello world!");
If we were to run this with node index.js
this wouldn’t be compiled with
Babel. So instead of doing that, we’ll setup babel-register
.
First install babel-register
.
$ npm install --save-dev babel-register
Next, create a register.js
file in the project and write the following code:
require("babel-register");
require("./index.js");
What this does is registers Babel in Node’s module system and begins compiling
every file that is require
‘d.
Now, instead of running node index.js
we can use register.js
instead.
$ node register.js
Note: You can’t register Babel in the same file that you want to compile. As node is executing the file before Babel has a chance to compile it.
require("babel-register"); // not compiled: console.log("Hello world!");
babel-node
If you are just running some code via the node
CLI the easiest way to
integrate Babel might be to use the babel-node
CLI which largely is just a
drop in replacement for the node
CLI.
Note that this is not meant for production use. It’s considered bad practice to deploy code that gets compiled this way. It is far better to compile ahead of time before deploying. However this works quite well for build scripts or other things that you run locally.
First make sure that you have babel-cli
installed.
$ npm install --save-dev babel-cli
Note: If you are wondering why we are installing this locally, please read the Running Babel CLI from within a project section above.
Then replace wherever you are running node
with babel-node
.
If you are using npm scripts
you can simply do:
{
"scripts": {
- "script-name": "node script.js"
+ "script-name": "babel-node script.js"
}
}
Otherwise you’ll need to write out the path to babel-node
itself.
- node script.js
+ ./node_modules/.bin/babel-node script.js
Tip: You can also use
npm-run
.
babel-core
If you need to use Babel programmatically for some reason, you can use the
babel-core
package itself.
First install babel-core
.
$ npm install babel-core
var babel = require("babel-core");
If you have a string of JavaScript you can compile it directly using
babel.transform
.
babel.transform("code();", options);
// => { code, map, ast }
If you are working with files you can use either the asynchronous api:
babel.transformFile("filename.js", options, function(err, result) {
result; // => { code, map, ast }
});
Or the synchronous api:
babel.transformFileSync("filename.js", options);
// => { code, map, ast }
If you already have a Babel AST for whatever reason you may transform from the AST directly.
babel.transformFromAst(ast, code, options);
// => { code, map, ast }
For all of the above methods, options
refers to
http://babeljs.io/docs/usage/options/.
You may have noticed by now that running Babel on its own doesn’t seem to do anything other than copy JavaScript files from one location to another.
This is because we haven’t told Babel to do anything yet.
Since Babel is a general purpose compiler that gets used in a myriad of different ways, it doesn’t do anything by default. You have to explicitly tell Babel what it should be doing.
You can give Babel instructions on what to do by installing plugins or presets (groups of plugins).
.babelrc
Before we start telling Babel what to do. We need to create a configuration
file. All you need to do is create a .babelrc
file at the root of your
project. Start off with it like this:
{
"presets": [],
"plugins": []
}
This file is how you configure Babel to do what you want.
Note: While you can also pass options to Babel in other ways the
.babelrc
file is convention and is the best way.
babel-preset-es2015
Let’s start by telling Babel to compile ES2015 (the newest version of the JavaScript standard, also known as ES6) to ES5 (the version available in most JavaScript environments today).
We’ll do this by installing the “es2015” Babel preset:
$ npm install --save-dev babel-preset-es2015
Next we’ll modify our .babelrc
to include that preset.
{
"presets": [
+ "es2015"
],
"plugins": []
}
babel-preset-react
Setting up React is just as easy. Just install the preset:
$ npm install --save-dev babel-preset-react
Then add the preset to your .babelrc
file:
{
"presets": [
"es2015",
+ "react"
],
"plugins": []
}
babel-preset-stage-x
JavaScript also has some proposals that are making their way into the standard through the TC39’s (the technical committee behind the ECMAScript standard) process.
This process is broken through a 5 stage (0-4) process. As proposals gain more traction and are more likely to be accepted into the standard they proceed through the various stages, finally being accepted into the standard at stage 4.
These are bundled in babel as 4 different presets:
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
Note that there is no stage-4 preset as it is simply the
es2015
preset above.
Each of these presets requires the preset for the later stages. i.e.
babel-preset-stage-1
requires babel-preset-stage-2
which requires
babel-preset-stage-3
.
Simply install the stage you are interested in using:
$ npm install --save-dev babel-preset-stage-2
Then you can add it to your .babelrc
config.
{
"presets": [
"es2015",
"react",
+ "stage-2"
],
"plugins": []
}
So you’ve compiled your code with Babel, but this is not the end of the story.
babel-polyfill
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
function addAll() {
return Array.from(arguments).reduce((a, b) => a + b);
}
Which turns into this:
function addAll() {
return Array.from(arguments).reduce(function(a, b) {
return a + b;
});
}
However, this still won’t work everywhere because Array.from
doesn’t exist
in every JavaScript environment.
Uncaught TypeError: Array.from is not a function
To solve this problem we use something called a
Polyfill. Simply put, a
polyfill is a piece of code that replicates a native api that does not exist in
the current runtime. Allowing you to use APIs such as Array.from
before they
are available.
Babel uses the excellent core-js as its polyfill, along with a customized regenerator runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
$ npm install --save babel-polyfill
Then simply include the polyfill at the top of any file that requires it:
import "babel-polyfill";
babel-runtime
In order to implement details of ECMAScript specs, Babel will use “helper” methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single “runtime” which gets required.
Start by installing babel-plugin-transform-runtime
and babel-runtime
:
$ npm install --save-dev babel-plugin-transform-runtime
$ npm install --save babel-runtime
Then update your .babelrc
:
{
"plugins": [
+ "transform-runtime",
"transform-es2015-classes"
]
}
Now Babel will compile code like the following:
class Foo {
method() {}
}
Into this:
import _classCallCheck from "babel-runtime/helpers/classCallCheck";
import _createClass from "babel-runtime/helpers/createClass";
let Foo = function () {
function Foo() {
_classCallCheck(this, Foo);
}
_createClass(Foo, [{
key: "method",
value: function method() {}
}]);
return Foo;
}();
Rather than putting the _classCallCheck
and _createClass
helpers in every
single file where they are needed.
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
Babel presets are simply collections of pre-configured plugins, if you want to do something differently you manually specify plugins. This works almost exactly the same way as presets.
First install a plugin:
$ npm install --save-dev babel-plugin-transform-es2015-classes
Then add the plugins
field to your .babelrc
.
{
+ "plugins": [
+ "transform-es2015-classes"
+ ]
}
This gives you much finer grained control over the exact transforms you are running.
For a full list of official plugins see the Babel Plugins page.
Also take a look at all the plugins that have been built by the community. If you would like to learn how to write your own plugin read the Babel Plugin Handbook.
Many plugins also have options to configure them to behave differently. For example, many transforms have a “loose” mode which drops some spec behavior in favor of simpler and more performant generated code.
To add options to a plugin, simply make the following change:
{
"plugins": [
- "transform-es2015-classes"
+ ["transform-es2015-classes", { "loose": true }]
]
}
I’ll be working on updates to the plugin documentation to detail every option in the coming weeks. Follow me for updates.
Babel plugins solve many different tasks. Many of them are development tools that can help you debugging your code or integrate with tools. There are also a lot of plugins that are meant for optimizing your code in production.
For this reason, it is common to want Babel configuration based on the
environment. You can do this easily with your .babelrc
file.
{
"presets": ["es2015"],
"plugins": [],
+ "env": {
+ "development": {
+ "plugins": [...]
+ },
+ "production": {
+ "plugins": [...]
+ }
}
}
Babel will enable configuration inside of env
based on the current
environment.
The current environment will use process.env.BABEL_ENV
. When BABEL_ENV
is
not available, it will fallback to NODE_ENV
, and if that is not available it
will default to "development"
.
Unix
$ BABEL_ENV=production [COMMAND]
$ NODE_ENV=production [COMMAND]
Windows
$ SET BABEL_ENV=production
$ [COMMAND]
Note:
[COMMAND]
is whatever you use to run Babel (ie.babel
,babel-node
, or maybe justnode
if you are using the register hook).Tip: If you want your command to work across unix and windows platforms then use
cross-env
.
Manually specifying plugins? Plugin options? Environment-based settings? All this configuration might seem like a ton of repetition for all of your projects.
For this reason, we encourage the community to create their own presets. This could be a preset for the specific node version you are running, or maybe a preset for your entire company.
It’s easy to create a preset. Say you have this .babelrc
file:
{
"presets": [
"es2015",
"react"
],
"plugins": [
"transform-flow-strip-types"
]
}
All you need to do is create a new project following the naming convention
babel-preset-*
(please be responsible with this namespace), and create two
files.
First, create a new package.json
file with the necessary dependencies
for
your preset.
{
"name": "babel-preset-my-awesome-preset",
"version": "1.0.0",
"author": "James Kyle <me@thejameskyle.com>",
"dependencies": {
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.3.15"
}
}
Then create an index.js
file that exports the contents of your .babelrc
file, replacing plugin/preset strings with require
calls.
module.exports = {
presets: [
require("babel-preset-es2015"),
require("babel-preset-react")
],
plugins: [
require("babel-plugin-transform-flow-strip-types")
]
};
Then simply publish this to npm and you can use it like you would any preset.
Babel is pretty straight forward to setup once you get the hang of it, but it can be rather difficult navigating how to set it up with other tools. However, we try to work closely with other projects in order to make the experience as easy as possible.
Newer standards bring a lot of new syntax to the language and static analysis tools are just starting to take advantage of it.
One of the most popular tools for linting is ESLint,
because of this we maintain an official
babel-eslint
integration.
First install eslint
and babel-eslint
.
$ npm install --save-dev eslint babel-eslint
Note:
babel-eslint
compatibility with Babel 6 is currently in a pre-release version. Install the latest 5.0 beta in order to use it with Babel 6.
Next create or use the existing .eslintrc
file in your project and set the
parser
as babel-eslint
.
{
+ "parser": "babel-eslint",
"rules": {
...
}
}
Now add a lint
task to your npm package.json
scripts:
{
"name": "my-module",
"scripts": {
+ "lint": "eslint my-files.js"
},
"devDependencies": {
"babel-eslint": "...",
"eslint": "..."
}
}
Then just run the task and you will be all setup.
$ npm run lint
For more information consult the
babel-eslint
or
eslint
documentation.
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects (@hzoo) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the --esnext
option. So integrating Babel is as easy as:
$ jscs . --esnext
From the cli, or adding the esnext
option to your .jscsrc
file.
{
"preset": "airbnb",
+ "esnext": true
}
For more information consult the
babel-jscs
or
jscs
documentation.
Using Babel, ES2015, and Flow you can infer a lot about your code. Using documentation.js you can generate detailed API documentation very easily.
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
All of the major JavaScript frameworks are now focused on aligning their APIs around the future of the language. Because of this, there has been a lot of work going into the tooling.
Frameworks have the opportunity not just to use Babel but to extend it in ways that improve their users’ experience.
React has dramatically changed their API to align with ES2015 classes
(Read about the updated API here).
Even further, React relies on Babel to compile it’s JSX syntax, deprecating it’s
own custom tooling in favor of Babel. You can start by setting up the
babel-preset-react
package following the
instructions above.
The React community took Babel and ran with it. There are now a number of transforms built by the community.
Most notably the
babel-plugin-react-transform
plugin which combined with a number of
React-specific transforms
can enable things like hot module reloading and other debugging utilities.
Introducing ES2015, JSX, and Flow syntax with Babel can be helpful, but if your text editor doesn’t support it then it can be a really bad experience. For this reason you will want to setup your text editor or IDE with a Babel plugin.
[WIP]
Babel has a very large and quickly growing community, as we grow we want to ensure that people have all the resources they need to be successful. So we provide a number of different channels for getting support.
Remember that across all of these communities we enforce a Code of Conduct. If you break the Code of Conduct, action will be taken. So please read it and be conscious of it when interacting with others.
We are also looking to grow a self-supporting community, for people who stick around and support others. If you find someone asking a question you know the answer to, take a few minutes and help them out. Try your best to be kind and understanding when doing so.
Discourse has provided us with a hosted version of their forum software for free (and we love them for it!). If forums are your thing please stop by discuss.babeljs.io.
Everyone loves Slack. If you’re looking for immediate support from the community then come chat with us at slack.babeljs.io.
Babel uses the awesome issue tracker provided by Phabricator an open source software development platform that makes GitHub issues a nightmare of the past.
Babel’s Phabricator is available at phabricator.babeljs.io. You can see all the open and closed issues on maniphest.
If you want to open a new issue:
Babel issues can sometimes be very difficult to debug remotely, so we need all the help we can get. Spending a few more minutes crafting a really nice bug report can help get your problem solved significantly faster.
First, try isolating your problem. It’s extremely unlikely that every part of your setup is contributing to the problem. If your problem is a piece of input code, try deleting as much code as possible that still causes an issue.
[WIP]