ホームページから React と Babel を外した理由
Why a one-page studio site was shipping 4.2 MB of JavaScript, and what it ships now.
On 2026-09-05 we replaced the React + Babel-standalone setup behind oddduck.ooo with plain HTML. The old page downloaded 4,226 KB of JavaScript — Babel 3,064 KB, React DOM 1,055 KB, React 107 KB — and compiled its JSX in the browser before painting anything; on a wired line that alone took 512 ms. The new page ships about 11 KB of vanilla JS and paints in under 100 ms on a local server. Nothing visible changed.
oddduck.ooo のホームは、2026年9月5日から React なしの静的 HTML で配信しています。以前は最初の1ピクセルが出るまでに JavaScript を 4,226 KB 受け取り、ブラウザで JSX をコンパイルしていました。今は 11 KB です。
発端は「接続に時間がかかりすぎる気がする」という一言でした。まずサーバーを疑いましたが、サーバーは問題ありませんでした。原因はホームページの作り方のほうにありました。
何が遅かったのか
事実 改修前のホームは、HTML の本文が事実上空でした。画面は外部 CDN からスクリプトが三つ届いたあとに React が描いていました。画面を作るコードは事前にコンパイルしていなかったので、訪問者のブラウザが毎回コンパイルしていました(2026年9月5日時点)。
事実 2026年9月5日、有線回線でブラウザのキャッシュを切り、三つのファイルを受け取り直した結果です。
| ファイル | サイズ | ダウンロード |
|---|---|---|
| @babel/standalone 7.29.0 | 3,064 KB | 290 ms |
| react-dom 18.3.1 (development) | 1,055 KB | 146 ms |
| react 18.3.1 (development) | 107 KB | 76 ms |
| 合計 | 4,226 KB | 512 ms |
三つの要因が重なっていました。第一に、開発用ビルドを使っていました。本番用ビルドなら React の二つが 1,162 KB から 140 KB ほどに減ります。第二に、3 MB の Babel は、JSX をブラウザでコンパイルするためだけにかかっていた費用です。第三に、本文が空だったので、これが全部届いてコンパイルまで終わってから最初の1ピクセルが描かれました。それまで訪問者はクリーム色の空白画面を見ていました。
有線でもダウンロードだけで0.5秒です。帯域がその5分の1の回線なら、単純計算で2.5秒です。ここに 3 MB のスクリプトを解析する時間と JSX をコンパイルする時間が、訪問者の端末の CPU の負担として上乗せされます。「時間がかかる」という体感とちょうど一致する構造でした。
念のために書くと、サーバー側は問題ではありませんでした。事実 同じ日に自分たちで測ったサーバーの最初の1バイトの応答は 100〜270 ms、画像を全部合わせても270 KB でした。
なぜそう作ったのか
このサイトは最初、デザインのプロトタイプとして始まりました。書体やコピーを差し替えながら見るために、画面の隅に調整パネル(tweaks panel)を付けました。それを早く作りたくて React を使い、JSX もブラウザでコンパイルしていました。ビルド段階を置かないという原則と、早く見たいという欲が重なると、こういう構造になります。プロトタイプがそのまま本番になっただけです。
事実 その調整パネルは、訪問者に一度も描画されたことがありません。それでもパネルのコード25.8 KB が、訪問のたびに一緒にコンパイルされていました。
何に置き換えたのか
ホームは1ページだけの紹介サイトです。状態と呼べるものは、モバイルメニューが開いているかどうかくらいしかありません。そこでフレームワークを替えるのではなく、フレームワークをなくしました。
- React が作っていた画面を、そのまま HTML に書きました。ブラウザは受け取った時点で描きます。
- メニュー、モバイルのドロワー、アヒルを押すと鳴る「クワッ」という声は、小さなスクリプト一つにまとめました。
- ステータス行とキーボードショートカットは、もともとあった小さなスクリプトにそのまま残しました。
- フォントは、スタイルの中から次々に読み込む方式をやめて、文書の先頭で直接受け取るようにしました。以前はスタイルを全部受け取ってから、フォントを順番に受け取っていました。
- 調整パネルと、コンパイルが必要なファイルは削除しました。
ビルド段階は今もありません。ファイルを直して上げれば、そのまま配信されます。守りたかったのは「ビルドなし」であって「React」ではありませんでした。
結果
事実 改修後、ローカルサーバー(フォントはキャッシュ済み)で First Contentful Paint 96 ms、DOMContentLoaded 53 ms。本番では初回訪問で DOMContentLoaded 361 ms。ホームが受け取るスクリプトは二つ、合わせて約 11 KB です(2026年9月5日計測)。
| 前 | 後 | |
|---|---|---|
| JavaScript | 4,226 KB + JSX 49 KB | 11 KB |
| ブラウザ内でのコンパイル | 訪問のたび | なし |
| 最初の1ピクセルまで | 全部届いてコンパイルしたあと | HTML が届いた時点 |
| ビルド段階 | なし | なし |
見えるものは何ひとつ変わっていません。色、書体、アヒル、鳴き声、マーキー、モバイルのドロワー、全部そのままです。変わったのは、それらが画面に届くまでの経路だけです。
学んだこと
プロトタイプ用の道具を本番にそのまま置いておくと、その費用は訪問者が払います。とくに「ビルドなし」は「バンドルなし」という意味ではありません。ブラウザでコンパイルするのはビルドをなくしたのではなく、訪問者ごとにビルドさせているということです。
そこで私たちの原則が一行増えました。React をもう一度入れないこと。 画面を変える理由ができたら、そのときにまた考えますが、紹介ページ一つに 4 MB のコンパイラを付ける理由はできないはずです。