— Blog · ノート

ホームページから React と Babel を外した理由

Why a one-page studio site was shipping 4.2 MB of JavaScript, and what it ships now.

TL;DR · English

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.03,064 KB290 ms
react-dom 18.3.1 (development)1,055 KB146 ms
react 18.3.1 (development)107 KB76 ms
合計4,226 KB512 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日計測)。

前後
JavaScript4,226 KB + JSX 49 KB11 KB
ブラウザ内でのコンパイル訪問のたびなし
最初の1ピクセルまで全部届いてコンパイルしたあとHTML が届いた時点
ビルド段階なしなし

見えるものは何ひとつ変わっていません。色、書体、アヒル、鳴き声、マーキー、モバイルのドロワー、全部そのままです。変わったのは、それらが画面に届くまでの経路だけです。

学んだこと

プロトタイプ用の道具を本番にそのまま置いておくと、その費用は訪問者が払います。とくに「ビルドなし」は「バンドルなし」という意味ではありません。ブラウザでコンパイルするのはビルドをなくしたのではなく、訪問者ごとにビルドさせているということです。

そこで私たちの原則が一行増えました。React をもう一度入れないこと。 画面を変える理由ができたら、そのときにまた考えますが、紹介ページ一つに 4 MB のコンパイラを付ける理由はできないはずです。