Astroで小さなインタラクティブ図解を作ろうとして、何気なくReactで書いたら、ページに180KB近いバンドルが乗ってしまった。「Astro Islandsで必要な部分だけJSを動かせるはず」と思っていたのに、思ったほど軽くなりません。
これは、Astro Islandsの「いつロードするか」と「何をロードするか」を混同したことで起きます。
Astro Islandsの仕組みと落とし穴
Astroはデフォルトで HTML を静的生成し、UIフレームワークのクライアントJSを送りません。client:* ディレクティブを付けたコンポーネントだけが、クライアントサイドでハイドレーションされます。
<MyChart client:visible type="ai-workflow" />
client:load client:idle client:visible client:only でハイドレーションのタイミングを選べます。visible ならビューポートに入ったとき、idle ならメインスレッドが空いたとき、only はSSRせずクライアント側だけで描画。
ここで気をつけたいのは、「いつロードするか」は選べても「何をロードするか」は選べないこと。<MyChart client:visible /> のようにReactコンポーネントを一つでも置けば、React + ReactDOMのバンドルがそのページに乗ります。
実測してみる
Astroが生成する dist を確認すると、React Islandがあるページでは次のようなチャンクが含まれます。
| チャンク | raw | gzip推定 |
|---|---|---|
| Reactクライアント(react-dom含む) | 182KB | 58KB |
| jsx-runtime | 0.5KB | <1KB |
| 自作コンポーネント(例: InteractiveFigure) | 4KB | 2KB |
「小さなインタラクティブ図解」が4KBでも、その背後のReact + ReactDOMが60KB近く乗ります。同じ図解をAstroのテンプレートと小さなバニラJSで書けば、合計5KBに収まることもある。差は10倍以上。
Reactが要るかを問い直す
「Astro IslandsがあるからなんでもReactで書こう」は、危険です。次の問いで振り分けると、Reactの出番がぐっと絞られます。
状態がページ寿命より長い必要があるか。Yesなら React (useState、context)、Noならテンプレートと一時的な data 属性で十分です。コンポーネント階層が3段以上ある複雑な props と event の流れか。YesならReact、NoならバニラなDOM操作で済みます。大量の再レンダリングが要るか (リスト、テーブル、diff更新)。YesならReact、NoならDOM直接更新で十分です。既存ライブラリがReact前提か。そのライブラリのためにReactを入れるか、それとも代替を探すか判断。
3ノードの図解、フォーム入力欄数個、開閉するドロワー。これらはすべてバニラで書ける範囲です。Reactを入れる根拠は薄い。
React不要時の代替
静的SVG + 小さなバニラJS
「クリックでハイライト切替」「キーボード操作でフォーカス移動」程度なら、SVGに data-* 属性を持たせ、Astroの <script> でイベントリスナーを貼るだけで足ります。
<svg viewBox="0 0 100 60">
<g data-node="ai" tabindex="0" role="button" aria-pressed="false">
<circle cx="50" cy="30" r="7" />
</g>
</svg>
<script>
document.querySelectorAll('[data-node]').forEach((g) => {
g.addEventListener('click', () => {
g.setAttribute('aria-pressed', 'true');
// ...
});
});
</script>
状態は aria-pressed や data-active 属性として、DOM側に持たせます。これだけでReactの代替になる。
Preact化
「React APIそのまま使いたいが、バンドルを小さくしたい」ならPreactと preact/compat alias を使います。React APIほぼ互換で、バンドルは10KB前後。Astroの @astrojs/preact インテグレーションを使えば、差し替えだけで動きます。
Web Components / Lit
将来React以外のフレームワークでも再利用する可能性があるなら、Web Components化が選択肢。Litを使えば5KB程度です。学習コストはやや高め。
Astro Islandsは「JavaScriptを必要な部分だけに絞る」仕組みであって、「Reactを自由に使っていい免罪符」ではありません。そのコンポーネントに本当にReactが必要か、毎回問い直す。静的SVGとバニラJSで書けるケースが、思ったより多いです。バンドル60KB削減は、モバイルのLighthouse Performanceスコアに直結します。