はじめに
書いたばかりのアロー関数やオプショナルチェイニングが、なぜか古いブラウザでは動かない。逆に、React全盛の今どきのプロジェクトを開いてみると、.babelrcやbabel.config.jsがひっそりと置いてあって、誰も中身を意識していない。そんな経験、ありませんか。
その裏側で黙々と働いているのがBabelです。最新の構文で書かれたJavaScriptを、対象ブラウザが理解できる形に変換してくれるコンパイラで、Create React AppやNext.js、Jestなど数え切れないツールの内部で使われ続けています。SWCやesbuildといった高速な代替コンパイラが登場した今でも、プラグインの豊富さと変換の正確さから、JSXやTypeScriptの構文解析を担う場面で根強く採用され続けているのがBabelです。
しかも2026年6月には、8年ぶりのメジャーバージョンとなるBabel 8が正式リリースされたばかり。「ESM専用配布への移行」と「デフォルトでのES5出力の廃止」という、Babelの前提を変える方針転換が入っています。週間6億5,000万ダウンロードを超える定番ツールがここまで大きく舵を切ったのは、地味に見えて結構なニュースです。
とはいえ、説明を読むより実際に動かした方が早いと思います。ソースコードを書き換えると変換結果がその場で変わるサンプルを用意したので、先に挙動を見たい方はこちらからどうぞ。
Babelとは
Babelは、JavaScriptのソースコードを別のJavaScriptコードに変換する「トランスパイラ」です。ES2015以降の構文で書かれたコードを解析してAST(抽象構文木)を組み立て、プラグインやプリセットを通してASTを書き換え、対象環境向けのコードとして出力し直します。
2014年に「6to5」という名前で誕生し、2015年に現在のBabelへと改名。以来、JavaScriptエコシステムの標準的なコンパイラ基盤として、React・Vue・Angularを問わず幅広いツールチェーンの土台になっています。
Babel 8での大きな変更点
2026年6月にリリースされたBabel 8では、次の2点が特に大きな変更です。
- ESM専用配布 - Babel本体のパッケージがCommonJSでの配布をやめ、ESM形式のみになりました
@babel/preset-envのデフォルトターゲット変更 - これまでのように何も指定しなければES5相当まで変換される、という挙動ではなくなり、targetsを省略するとBrowserslistのdefaultsクエリ(主要ブラウザの直近バージョン)が使われるようになりました。ES5まで変換したい場合は、targetsにie 11のような古いブラウザを明示する必要があります
この記事のサンプルでも、意図的に古いブラウザ向けのtargetsを指定して変換結果を分かりやすくしています。
主な特徴
- プラグイン・プリセットのエコシステム -
@babel/preset-envでターゲットブラウザに応じた変換を自動化でき、@babel/preset-reactや@babel/preset-typescriptでJSXやTypeScript構文にも対応できます - ASTベースの変換基盤 - コードをASTとして扱うため、正規表現では不可能な正確な構文変換が可能で、独自プラグインを書けば好きな変換ロジックを追加できます
- エコシステム全体からの採用実績 - Create React App、Next.js、Gatsby、Jestなど主要ツールの内部で長年使われ続けており、動作の枯れ具合と情報量の多さが強みです
インストール
Node.jsプロジェクトへの導入は@babel/coreと用途に応じたプリセットを組み合わせます。
# npm
npm install --save-dev @babel/core @babel/cli @babel/preset-env
# yarn
yarn add -D @babel/core @babel/cli @babel/preset-env
# pnpm
pnpm add -D @babel/core @babel/cli @babel/preset-env
ブラウザ上で直接動かしたいだけなら、ビルド不要の@babel/standaloneを使う方法もあります(後述のサンプルはこちらを利用しています)。
Babelのサンプルを動かす
下のサンプルは、@babel/standaloneが提供するBabel.transform()にソースコードとプリセットを渡し、変換結果をリアルタイムに表示するものです。テキストエリアの中身を書き換えると、Babelが吐き出すコードがその場で変わります。
要点だけを抜き出すと、Babelの変換はこの数行に集約されます。presetsにenvを指定し、targetsで古いブラウザを狙うと、アロー関数やlet/const、テンプレートリテラルといったES2015以降の構文が、その環境でも動く構文に変換されます(Babel 8ではtargetsを省略すると主要ブラウザの直近版がターゲットになり、ES5までは変換されなくなった点に注意してください)。
// アロー関数・const・テンプレートリテラルを含むソース
const input = 'const greet = (name) => `Hello, ${name}!`;';
// preset-env + targets:"ie 11" でES5相当のコードに変換
const output = Babel.transform(input, {
presets: [["env", { targets: "ie 11" }]],
}).code;
console.log(output);
// => "use strict";
// var greet = function greet(name) {
// return "Hello, ".concat(name, "!");
// };
実際にソースコードを書き換えて確かめられるのが次のサンプルです。テキストエリアの中身を、たとえばconstをletに変えたり、アロー関数を増やしたりしてみてください。ターゲットにie 11を指定しているので、preset-envがES5相当まで変換してくれます。
const greet = (name) => \Hello, ${name}!`;が、var宣言とfunction式、String.prototype.concatを使ったコードに変換されるのが確認できるはずです。テキストエリアに構文エラーのあるコードを入れると、Babelのパーサがエラーメッセージを返してくる様子も見られます。この「ASTを読み取って書き換え、コードとして出力し直す」という一連の流れが、Babelのすべてのプラグイン・プリセットに共通する基本動作です。なおtargets`をこのサンプルから外すと、Babel 8ではモダンブラウザ向けの構文がほぼそのまま出力されることも試してみると分かります。
基本的な使い方
実際のプロジェクトでは、babel.config.js(または.babelrc)に設定を書き、CLIやバンドラー経由で変換をかけるのが一般的な使い方です。
// babel.config.js
module.exports = {
presets: [
["@babel/preset-env", { targets: "defaults" }],
],
};
# src配下のファイルをlib配下にトランスパイルして出力
npx babel src --out-dir lib
Node.jsから直接呼び出したい場合は@babel/coreのtransformSyncを使います(ファイルシステムやNode.js APIに依存するため、この例はブラウザ上では実行できません)。
const babel = require("@babel/core");
const { code } = babel.transformSync("const add = (a, b) => a + b;", {
presets: ["@babel/preset-env"],
});
console.log(code);
実践的なユースケース
Babelの真価は「preset-envで古いブラウザ向けに変換する」だけにとどまりません。JSXやTypeScriptといった、ブラウザがそもそも解釈できない構文をJavaScriptに変換したり、独自のプラグインでASTそのものを書き換えたりできる点が、他のツールにはない強みです。
JSXをJavaScriptに変換する
Reactで書く<div>Hello</div>のようなJSXは、ブラウザにとってはただの構文エラーです。@babel/preset-reactを使うと、JSXをReact.createElement()の呼び出しに変換して、通常のJavaScriptとして実行できるようにします。
import Babel from "@babel/standalone@8.0.4";
const jsx = 'const el = <h1 className="title">Hi</h1>;';
// preset-react がJSXをReact.createElement呼び出しに変換する
Babel.transform(jsx, { presets: ["react"] }).code;
// => var el = /*#__PURE__*/React.createElement("h1", { className: "title" }, "Hi");
下のサンプルでは、JSXのタグ名や属性を書き換えると、変換後のReact.createElement()呼び出しがどう変わるかを確認できます。
タグ名をh1からbuttonに変えたり、属性を増やしたりすると、React.createElement()の第1引数(タグ名)と第2引数(propsオブジェクト)がそのまま追従して変わることが分かります。バンドラーが裏で行っているJSX変換は、まさにこの処理です。
TypeScriptの型注釈を取り除く
TypeScriptのコードをブラウザで実行するには、型チェックそのものではなく「型注釈を取り除く」処理が必要です。@babel/preset-typescriptは型チェックをせず、型に関する部分をコードから単純に削除するだけの軽量な変換を行います。
import Babel from "@babel/standalone@8.0.4";
const ts = "function double(n: number): number { return n * 2; }";
// preset-typescript は型チェックをせず、型注釈を取り除くだけ
Babel.transform(ts, { presets: ["typescript"] }).code;
// => function double(n) { return n * 2; }
次のサンプルでは、型注釈のついたTypeScriptコードを書き換えると、型情報が取り除かれた素のJavaScriptがどう出力されるかを確認できます。
interface Userの宣言そのものが出力から消え、greet関数の引数と戻り値についていた: Userや: stringといった型注釈だけがきれいに取り除かれるのが分かります。preset-typescriptは型の整合性までは見てくれないので、実際の開発では別途tscによる型チェックと組み合わせるのが定石です。
カスタムプラグインでASTを書き換える
Babelの拡張性の核心は、プリセットの奥にある「プラグイン」です。プラグインはASTを訪問(visit)しながらノードを書き換える関数で、Babel.registerPlugin()を使えばブラウザ上でも自作のプラグインを試せます。
import Babel from "@babel/standalone@8.0.4";
// Identifierノード(変数名・関数名など)を訪問して書き換えるプラグイン
function renamePlugin() {
return {
visitor: {
Identifier(path) {
if (path.node.name === "oldName") {
path.node.name = "newName";
}
},
},
};
}
Babel.registerPlugin("rename-old-name", renamePlugin);
Babel.transform("const oldName = 1;", { plugins: ["rename-old-name"] }).code;
// => "const newName = 1;"
下のサンプルでは、console.logの呼び出しをすべてconsole.infoに置き換えるプラグインを、CallExpressionノードを訪問する形で実装しています。入力コードの中のconsole.logを増やしたり、別のメソッド名に変えたりして挙動を確かめてみてください。
console.logと書いた箇所だけがconsole.infoに置き換わり、doSomething()のような無関係な呼び出しはそのまま残ることが確認できます。このように「特定のノードだけを狙って書き換える」のがBabelプラグインの基本パターンで、実際のLintの自動修正ツールやコード変換ツール(codemod)の多くも、この仕組みの上に成り立っています。
まとめ
Babelは、最新のJavaScript構文を古いブラウザ向けに変換するだけのツールではなく、JSXやTypeScriptといった非標準の構文をJavaScriptに変換したり、ASTを直接書き換えるカスタムプラグインを作れたりする、拡張性の高いコンパイラ基盤です。SWCやesbuildのような高速な代替が登場した今でも、プラグインエコシステムの豊富さから、多くのツールチェーンの内部で使われ続けています。
まずは@babel/standaloneでサンプルのようにブラウザ上で試してみて、感触をつかんでから、実際のプロジェクトにbabel.config.jsを組み込んでみるとよいでしょう。独自の構文変換やコード変換ツールを作りたくなったら、Babel.registerPlugin()によるカスタムプラグインの仕組みも覗いてみてください。