AstroはWebサイトを組み立てるフレームワークで、ReactはUIを作るライブラリです。両者は競合ではなく、Astroは公式の@astrojs/react統合でReactコンポーネントを「島(アイランド)」としてページに埋め込めます。違いの本質は、JavaScriptをブラウザへいつ・どれだけ送るかにあります。この記事では2026年10月14日時点の現行版(astro 7.3.8/@astrojs/react 7.0.1/react 19.3.0)で実際にビルドして測った数値をもとに、両者の違い、どちらを主役にするかの判断基準、AstroでReactを動かす手順、島をまたぐ状態共有の方法までを説明します。Astro自体の特徴はAstroフレームワークとは?特徴と使い方・Next.jsとの使い分けを解説【2026年版】で扱っています。
まとめ:AstroとReactの違いと使い分けの結論
- 違いはJavaScriptの配り方。ReactのCSRアプリはUIをブラウザで組み立て(SSRやServer Componentsを使う構成もある)、AstroはHTMLを先に出して
client:*を付けた部品だけを動かす。 - Reactの島を1つでも置くと、Reactのランタイムは丸ごと配られる。実測では島1つで約59.9kB(brotli)となり、同じ部品だけのVite製SPA(約58.4kB)とほぼ同じだった。ランタイムの負担は島でも残り、Astroで減らせるのは静的に出せる部分のコードと、Reactを置かないページのJavaScriptである。
- 選び分けの基準は、画面全体で複雑な状態管理とクライアントルーティングが要るか。管理画面・エディタ・チャットのようにアプリ全体の連携が中心ならReact主体(Next.js等)、記事・LP・ドキュメント・コーポレートサイトのようにページ単位で完結するならAstro主体。
- 導入は
npx astro add reactの1コマンド。@astrojs/react 7(2026年9月22日)でbabelオプションが廃止され、指定したままだとビルドが止まる。代わりにReact Compilerを有効にするcompilerオプションが入った。 - 島どうしはReact Contextで値を渡せない。.astroの中でProviderを置いても子の島には届かない。共有は公式レシピが推すNano Storesで行う。
AstroとReactの違い:JavaScriptの配り方と役割分担
ReactはUIライブラリで、ルーティングやビルドは別の道具(Vite、Next.jsなど)が受け持ちます。Astroはルーティング・ビルド・HTML生成までを持つフレームワークで、UI部品の作成にReactやVue、Svelteを呼び出せます。同じ層の製品ではないため、「どちらが速いか」ではなく「どこまでをブラウザのJavaScriptに任せるか」で比べると違いがはっきりします。描画方式の用語はSSR・CSR・SSG・ISG・ISRの違いと使い分け|定義・SEO・速度を比較で整理しています。
| 観点 | Astro | React(SPA/Next.js) |
|---|---|---|
| 位置づけ | Webフレームワーク | UIライブラリ(+Next.js等) |
| 既定のクライアントJS | 0バイト | ランタイム+クライアント実行部分 |
| 動かす単位 | 島(client:*を付けた部品) |
アプリ全体のツリー |
| ルーティング | src/pages/のファイルベース |
React Router/App Router |
| 他のUIライブラリ | Vue・Svelte・Preact等と併用可 | Reactに統一 |
| 向く用途 | 記事・LP・ドキュメント・EC商品ページ | 管理画面・SaaS・エディタ |
Astroは、client:*ディレクティブを付けない限りReactコンポーネントもHTMLとして書き出すだけで、そのJavaScriptを配りません(公式のディレクティブリファレンス)。HTMLの大部分を静的に出し、動かしたい部分だけをJavaScriptで復元する考え方をアイランドアーキテクチャと呼びます。仕組みの詳細はアイランドアーキテクチャとは?仕組み・Astroの実装・PPRとの違いを参照してください。
React島1つで配られるJavaScript量の実測
「Astroは軽い」という説明は、どの条件で軽いのかが抜けがちです。そこで、useStateでカウントするだけの同じCounter.tsxを使い、置き方ごとにブラウザへ送られるJavaScriptを測りました。数値は各ページのHTMLから読み込まれるJSファイルとその依存ファイルを、1ファイルずつ圧縮したサイズの合計です(HTMLに埋め込まれたスクリプトとHTTPヘッダーは含みません)。
| 構成 | JSファイル数 | brotli | gzip |
|---|---|---|---|
Astro:client:*なし |
0 | 0 | 0 |
Astro:client:loadの島1つ |
4 | 59,940B | 69,185B |
Astro:client:loadの島3つ |
4 | 59,940B | 69,185B |
| Astro:島2つ+Nano Stores | 6 | 60,996B | 70,323B |
| Vite 8+ReactのSPA(同じ部品) | 1 | 58,372B | 67,758B |
| Astro:Preact(compat)の島1つ | 4 | 8,763B | 9,627B |
計測環境はNode.js 26.5.0、astro 7.3.8、@astrojs/react 7.0.1、react 19.3.0、vite 8.3.4、@astrojs/preact 6.0.6(preact 10.29.8)です。brotliは品質11、gzipはレベル9で圧縮しました。Astroの島があるページには、このほかにハイドレーション用のインラインスクリプトが約5KB(未圧縮)入ります。
表から読み取れることは3つあります。1つ目に、Reactの島を1つ置いた時点でReactとReact DOMのランタイムが丸ごと乗り、その量は同じ部品だけのSPAとほぼ同じです(ファイルが4つに分かれる分、SPAより約1.6kB多くなりました)。島1つの中身だけが配られるわけではありません。2つ目に、今回のように同じCounterを3か所に置く場合は、コンポーネントとランタイムのJSを共有できます。異なる部品や依存ライブラリを追加すれば、その分の配信量は増えます。3つ目に、client:*を付けないページは0バイトです。Astroの軽さは「Reactを置かないページを作れること」と「HTMLを先に出せること」から来ています。
全ページにReactの島があっても、静的な部分のコンポーネントや依存ライブラリをクライアント配信から外せれば、JavaScriptの量は減らせます。島の中身がボタンやフォーム程度で済むなら、同じ.tsxのまま@astrojs/preactのcompat: trueで動かすと、約8.8kBと約7分の1に収まりました。Preactへの切り替えの注意点はPreactとは?Reactとの違い・導入手順・compat移行と採用判断【2026年9月版】で扱っています。
Astro主体・React主体の選定基準
判断軸は、画面全体で複雑な状態管理とクライアントルーティングが必要かどうかです。Astroでも<ClientRouter />とtransition:persistを使えば島の状態をページ遷移後に残せるため(公式のView Transitionsガイド)、状態を保持したいという理由だけでは除外しません。エディタや管理画面のように、多数の部品が同じ状態を読み書きし、画面遷移もブラウザ側で制御するアプリが中心なら、React主体(Next.jsを含む)を選びます。反対に、ページが互いに独立していて、訪問者の多くが数ページで離脱するコンテンツ型のサイトでは、全ページにReactのランタイムを配る理由がなく、Astroが有利です。企業サイト制作・コーポレートサイト制作のように、ページの大半が読み物で操作部品が限られるサイトは、HTMLを先に出して必要な部品だけを動かすAstroの設計と合致します。
両者を1つのドメインで分けて使う構成も取れます。マーケティングサイトをAstro、ログイン後のアプリをReactで別に配信し、パスで振り分ける方法です。Reactの開発環境はViteとは?Reactの環境構築手順とVite 8の変更点・create-react-appからの移行、Reactのフルスタック構成はNext.js 16の最新バージョンは16.3|リリース履歴・LTS方針・アップデート手順が参考になります。
AstroでReactを使う手順:@astrojs/react 7の導入
AstroのプロジェクトでReactを動かすには、公式統合@astrojs/reactを追加します。既存の.jsx/.tsxコンポーネントはほぼそのまま使えます(@astrojs/reactの公式ガイド)。
npx astro add reactで変わる3つのファイル
astro 7.3.8のプロジェクトで次のコマンドを実行すると、依存パッケージのインストール、astro.config.mjsへの追記、tsconfig.jsonのJSX設定までが自動で行われます。
npx astro add react
このとき実行されたインストールコマンドはnpm i @astrojs/react@^7.0.1 react@^19.3.0 react-dom@^19.3.0 @types/react@^19.3.0 @types/react-dom@^19.3.0でした。設定ファイルは次の内容に書き換わります。
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react()]
});
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"],
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}
手動で導入する場合は、この2ファイルを自分で書き換えます。Astroの既定のastro/tsconfigs/baseは"jsx": "preserve"でjsxImportSourceを持たないため、compilerOptionsの2行を忘れるとエディタで型エラーになります。必要なNode.jsはastro 7.3.8・@astrojs/react 7.0.1とも>=22.12.0です。
client:*ディレクティブ別の読み込み・ハイドレーション時期
Astroに置いたReactコンポーネントは、client:*を付けて初めてブラウザで動きます。どれを選ぶかが、そのままページの初期表示の重さに響きます。
| ディレクティブ | ハイドレートする時期 | 向く用途 |
|---|---|---|
client:load |
ページ読み込み直後 | ファーストビューの操作部品 |
client:idle |
ブラウザのアイドル時 | 優先度の低いUI |
client:visible |
画面内に入ったとき | ページ下部の重いUI |
client:media |
メディアクエリ一致時 | モバイル専用メニュー |
client:only |
サーバー描画せずブラウザのみ | windowに依存する部品 |
例として、前の実測にも使った次のsrc/components/Counter.tsxを島にします。
// src/components/Counter.tsx
import { useState } from 'react';
export default function Counter({ label = 'count' }: { label?: string }) {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{label}: {n}</button>;
}
---
import Counter from '../components/Counter';
---
<Counter client:load />
<Counter client:idle={{timeout: 500}} />
<Counter client:visible={{rootMargin: "200px"}} />
<Counter client:media="(max-width: 50em)" />
<Counter client:only="react" />
client:idleのtimeoutはrequestIdleCallback()に渡されるミリ秒で、アイドル時に実行されないまま期限を迎えるとコールバックの実行がキューに入ります。その後にJSの読み込みとハイドレーションが行われるため、指定時間での完了は保証されません。client:visibleのrootMarginはIntersectionObserverに渡す余白で、200pxなら要素が画面に入る200px手前で読み込みが始まります。client:onlyはサーバーでHTMLを出さないため、"react"のようにフレームワーク名の指定が必須です。サーバー描画とブラウザ描画の結果が食い違う場合の原因の探し方はハイドレーションエラー(Hydration Error)とは?原因とReact / Next.jsでの対処法を解説にまとめています。
前の実測のとおり、client:visibleにしても、島が表示された場合に配られるJavaScriptの量はclient:loadと同じです(どちらも59,940B)。ただし画面内に入らない島は読み込まれないため、訪問中の実転送量が減る場合はあります。ファーストビューの表示を軽くしたいときに使い、総量を減らすには、静的化できる部品とその依存コードを減らします。
@astrojs/react 7の変更点:babelオプション廃止とReact Compiler
@astrojs/react 7.0.0(2026年9月22日公開)は、JSXの変換とFast Refreshを@vitejs/plugin-react v6のOxcに切り替え、統合のbabelオプションを削除しました(@astrojs/reactのCHANGELOG)。react({ babel: { plugins: [] } })を残したままastro 7.3.8でビルドすると、次のメッセージで終了コード1になりました。
The @astrojs/react babel option has been removed. Configure @rolldown/plugin-babel in vite.plugins for custom Babel transforms.
styled-componentsなどのBabelプラグインを使っている場合は、@rolldown/plugin-babelと@babel/coreを入れ、設定をvite.pluginsへ移します。babel.config.jsや.babelrcは読み込まれないので、オプションはbabel()に直接渡します。
// npm install -D @rolldown/plugin-babel @babel/core
import react from '@astrojs/react';
import babel from '@rolldown/plugin-babel';
import { defineConfig } from 'astro/config';
export default defineConfig({
integrations: [react()],
vite: {
plugins: [babel({ plugins: ['babel-plugin-styled-components'] })],
},
});
入れ替わりに追加されたのがcompilerオプションです。oxc-transform-reactを入れてreact({ compiler: true })とすると、Oxc版のReact Compilerがクライアント側のコンポーネントとフックを自動でメモ化します。公式ガイドでは実験的な機能と位置づけられ、サーバー描画には適用されません。React 17/18ではreact-compiler-runtimeの追加が必要です。
npm install oxc-transform-react
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
integrations: [react({ compiler: true })],
});
手元のCounter.tsxで試すと、出力に6要素のメモ化キャッシュ(c(6))が挿入され、島のJavaScriptは59,940Bから60,127B(brotli)へ187B増えました。今回測ったのは配信サイズであり、再描画時間の改善効果は未測定です。そのため、効果を確かめる対象は再描画の多い一覧やフォームに絞るのが妥当です。React Compilerそのものの仕組みはReact 19とは?18からの変更点・新機能とReact Compiler・最新バージョンを解説で説明しています。
島をまたぐ状態共有:React Contextが届かない理由とNano Stores
Astroの島は、それぞれが独立したReactのルートとしてマウントされます。公式ドキュメントも、Astroの中で部分的にハイドレートする場合はContextのラッパーを使えないと明記しています(Share state between islands)。
実際に、値を"dark"にするProviderでuseContextを読む部品を囲み、.astroファイルの中に置いてビルドしました。
// src/components/ThemeContext.tsx
import { createContext, useContext, type ReactNode } from 'react';
export const Theme = createContext('default');
export function ThemeProvider({ children }: { children?: ReactNode }) {
return <Theme.Provider value="dark">{children}</Theme.Provider>;
}
export function ThemeLabel() {
const t = useContext(Theme);
return <span>theme={t}</span>;
}
---
import { ThemeProvider, ThemeLabel } from '../components/ThemeContext';
---
<ThemeProvider><ThemeLabel /></ThemeProvider>
<ThemeProvider client:load><ThemeLabel client:load /></ThemeProvider>
出力されたHTMLでは、どちらのThemeLabelもtheme=default(createContextの初期値)になり、darkは届きませんでした。.astroの中で子として書いた要素はProviderの子ツリーではなく、スロットとしてHTMLで受け渡されるためです。ページ上ではProviderで囲んで見えても、Contextは.astroの境界を越えません。
公式レシピは、島どうしで共有するクライアント側の状態にNano Storesを推奨しています。理由として、配信するJavaScriptが1KB未満で依存がないこと、Reactに限らずVueやSvelteの島とも同じストアを読めることが挙げられています。
npm install nanostores @nanostores/react
// src/stores/cart.ts
import { atom } from 'nanostores';
export const cartCount = atom(0);
// src/components/CartButton.tsx
import { useStore } from '@nanostores/react';
import { cartCount } from '../stores/cart';
export default function CartButton() {
const count = useStore(cartCount);
return <button onClick={() => cartCount.set(cartCount.get() + 1)}>カートに追加({count})</button>;
}
nanostores 1.5.5と@nanostores/react 2.0.1で、表示用のCartBadge(client:load)と追加ボタンのCartButton(client:visible)を別々の島にした構成は、島1つだけのページより1,056B(brotli)増えただけでした。どうしてもContextを使いたい場合は、共有したい範囲を1つのReactコンポーネントにまとめ、Providerごとその1つを島にします。ページ全体をProviderで包む必要が出てきたら、それはAstroよりReact主体で作るべきサインです。Nano Storesの永続化やcomputedはNano Storesとは?atom・computed・永続化の使い方とReact/Astro連携【1.5.4対応】、Context自体の使い方はReact Contextの使い方|createContext・useContextの実装とReact 19の新記法で扱っています。
Astroは言語ではない:.astroファイルとReactの書き方の差
Astroはプログラミング言語ではなくWebフレームワークです。「astro 言語」と検索されることがありますが、独自の言語ではありません。HTMLとJavaScript/TypeScriptの知識をそのまま使え、新しく学ぶのはAstro固有のテンプレート構文とディレクティブです。.astroファイルは、上部の---で囲んだフロントマターにJavaScript/TypeScriptを書き、その下にHTMLを書く形式です。
---
const posts = [{ title: 'AstroとReactの違い' }, { title: 'AstroでReactを使う方法' }];
---
<ul>
{posts.map((post) => <li class="post">{post.title}</li>)}
</ul>
<style>
.post { line-height: 1.8; }
</style>
Reactとの書き方の差は3点です。フロントマターのコードはビルド時(SSRならリクエスト時)にサーバーで1回だけ実行され、ブラウザには送られません。そのためuseStateのような状態は持てません。属性はclassNameではなくclassのまま書け、<style>は既定でそのコンポーネントにスコープされます。Astro 7.0では.astroのコンパイラがGo製からRust製に置き換わり、閉じていないHTMLタグはエラーになるなど構文の扱いが厳しくなりました(Astro 7の公式ブログ、astroのCHANGELOG)。
Astro 7とCloudflare傘下入り:2026年の変化でReact利用者に関係する点
2026年1月16日、CloudflareはAstroを開発するThe Astro Technology Companyが同社に加わると発表し、公式ブログの買収(Acquisitions)分類に掲載しました。発表には、AstroがMITライセンスのオープンソースのまま、公開ロードマップとオープンガバナンスを続けると書かれています(Cloudflareの発表)。Cloudflare以外へのデプロイも引き続き可能です。
バージョンは2026年に2回メジャーが上がりました。React利用者が影響を受ける点は次のとおりです。
| 版(公開日) | React利用者に関係する変更 |
|---|---|
| Astro 6.0(2026-03-10) | 開発サーバーをVite Environment APIで再設計 |
| Astro 7.0(2026-06-22) | Vite 8・Rust製コンパイラ・Node 22.12以上 |
| @astrojs/react 6.0.0(2026-06-22) | Vite 8対応(Astro 7と同時公開) |
| @astrojs/react 7.0.0(2026-09-22) | babelオプション削除・compiler追加 |
Markdownを多用するサイトでは、Astro 7.0で既定のMarkdown処理がRust製のSätteriに替わった点にも注意が必要です。remark/rehypeのプラグインを使い続けるには、@astrojs/markdown-remarkを入れて処理系に指定し直します。Astro 6.0ではFonts API、Live Content Collections、CSP対応も入りました(Astro 6の公式ブログ)。旧版のまま運用している場合の移行はAstro 5の新機能と移行手順|Content Layer・Server Islands・hybrid廃止をビルドで検証とAstro 5.16の変更点|SVGO最適化・previewショートカットと6/7系での扱いを参照し、導入時点の版はnpmのリリース一覧で確認してください。
Astroを選ぶべきでない場面と移行が失敗するパターン
次のいずれかに当てはまるなら、Astroへの移行は見送るべきです。
- 画面全体で状態とルーティングが連携するアプリが中心:SaaSの管理画面、エディタ、チャット、リアルタイムダッシュボード。
transition:persistで個々の島の状態は残せますが、アプリ全体の状態管理とクライアントルーティングを前提に作ったSPAの設計とはぶつかります。 - React Routerのクライアントルーティングに依存している:Astroのルーティングは
src/pages/のファイルで決まります。既存のBrowserRouterをブラウザ専用のまま移すならclient:onlyの島に閉じ込めることになり、その場合はサーバーでHTMLが出ず、実測のとおりSPAと同じ量のJavaScriptを配ることになります。 - どのページにもReactの島が必要:島1つで約59.9kBのランタイムが乗ります。静的な部分のコードを配信から外す効果は残りますが、ランタイムの負担は変わらないため、移行の手間に見合うかを先に見積もります。
Astroの採用判断では、人気の高低よりも、コンテンツ表示とアプリ操作のどちらが中心かを確認します。コンテンツ主体のサイトと業務アプリでは求められる機能が異なるため、自社の要件で比較します。具体的には、上の3条件に自社の画面が当てはまるかどうかを見ます。移行後に表示速度を詰める手順はAstroの画像最適化とパフォーマンス改善|astro:assetsとprefetchの実装にまとめています。
よくある質問
AstroとReactはどちらが速いですか?
Astroは不要なクライアントJSを省けますが、初期表示が速くなるかは画像、CSS、サーバー応答、比較するReact側の構成にも左右されます。実測では、Astroに置いたReactの島1つで約59.9kB(brotli)、同じ部品だけのReact SPAで約58.4kBと、ReactのJavaScriptの量はほぼ同じでした。「Astroだから速い」のではなく、Reactを動かす範囲を絞ることで、初期表示の負荷を減らせます。
AstroでReact 19は使えますか?
使えます。@astrojs/react 7.0.1の対応範囲はreact ^17.0.2 || ^18.0.0 || ^19.0.0で、2026年10月時点のnpx astro add reactはreact 19.3.0をインストールします。React 19のuseActionState()とAstro ActionsをつなぐwithState()/getActionState()も、@astrojs/react/actionsから提供されています。
Astroで使えるプログラミング言語は何ですか?
.astroファイルのフロントマターで書くのはJavaScriptとTypeScriptです。UI部品はReact・Vue・Svelte・Solid・Preactなどを統合経由で使え、コンテンツはMarkdownとMDXで書けます。PHPやPythonのようなサーバー言語をAstroの中で実行することはできません。
AstroはReactをベースにしたフレームワークですか?
いいえ。AstroはReactに依存しない独立したフレームワークで、Reactは@astrojs/reactを追加したときだけ使われます。統合を入れていないプロジェクトにはReactのパッケージが含まれません。Next.jsやRemixのようにReactを土台にしたフレームワークとは、この点が異なります。
既存のReactコンポーネントをAstroへそのまま移植できますか?
描画とローカルなuseStateで完結するコンポーネントは、ファイルをコピーしてclient:*を付けるだけで動きます。手直しが必要になるのは、上位のContextから値を受け取るもの、React RouterのuseNavigateなどに依存するもの、アプリ全体のストアを前提にしたものです。いずれも島の外に依存しているため、Nano Storesへの置き換えか島の切り方の見直しが必要です。