AstroでReactを使う方法:導入・活用ガイド

はい、承知いたしました。AstroでReactを使う方法について、導入から活用までを詳細に解説した記事を約5000語で記述します。


AstroでReactを使う方法:導入・活用ガイド

はじめに:新しいウェブ開発の選択肢、AstroとReact

現代のウェブ開発において、高速なウェブサイトの構築は最優先事項の一つです。ユーザーは高速でレスポンシブな体験を求め、検索エンジンはサイトパフォーマンスをランキングの重要な指標としています。一方で、リッチなUIや複雑なインタラクティブ性を持つウェブアプリケーションの需要も高まっており、React、Vue、SvelteといったモダンなJavaScriptフレームワークはその中心的な存在となっています。

しかし、これらのフレームワークを多用したシングルページアプリケーション(SPA)は、初期ロード時のJavaScriptバンドルサイズが大きくなりがちで、特に低速なネットワーク環境や非力なデバイスではパフォーマンスの問題を引き起こすことがあります。ページ全体がクライアントサイドでレンダリングされるまで時間がかかり、コンテンツの表示が遅れる「白画面」問題や、インタラクティブになるまでのタイムラグが発生することがあります。

ここで登場するのが Astro です。Astroはパフォーマンスを最優先に設計された、新しい世代のウェブサイト構築ツールです。静的サイト生成(SSG)やサーバーサイドレンダリング(SSR)を強力にサポートし、必要最低限のJavaScriptしかクライアントに送信しない「Island Architecture(アイランドアーキテクチャ)」を採用しています。これにより、驚くほど高速なウェブサイトを実現できます。

そして、Astroの素晴らしい点は、そのパフォーマンスメリットを享受しながら、あなたが使い慣れたUIフレームワーク(React、Vue、Svelte、Solid、Litなど)をシームレスに統合できることです。この記事では、特に世界で最も広く使われているUIライブラリの一つである React に焦点を当て、AstroプロジェクトでReactコンポーネントをどのように導入し、活用していくかを詳細に解説します。

あなたはすでにReactのエコシステムに慣れ親しんでいるかもしれませんし、豊富なReactコンポーネントライブラリや既存のコードベースを持っているかもしれません。Astroを使うことで、これらの資産を無駄にすることなく、超高速な静的サイトやサーバーレンダリングされたページの中に、必要な部分だけインタラクティブなReactコンポーネントを配置できます。これは、パフォーマンスと開発効率、そして豊かなユーザー体験を両立させる強力なアプローチです。

本記事では、AstroとReactを組み合わせる際のメリットから始まり、具体的な導入方法、基本的な使い方、インタラクティブ性の追加、状態管理、データの取得、スタイリング、パフォーマンス最適化、さらにはTypeScriptの利用やデプロイまで、幅広くカバーします。約5000語のボリュームで、あなたがAstroでReactをマスターするための詳細なガイドとなることを目指します。

さあ、パフォーマンスと開発体験を両立させるAstro + Reactの世界に飛び込みましょう。

Astroとは? パフォーマンスを追求した新時代のウェブツール

Astroについて詳しく見ていきましょう。Astroは、特にコンテンツリッチなウェブサイト、ブログ、マーケティングサイト、ドキュメンテーションサイト、eコマースサイトなどの構築に最適化されています。その核心にあるのは、前述の「Island Architecture」という考え方です。

Astroの哲学:Island Architectureと「JavaScript Zero」への挑戦

従来のSPAフレームワークが、ページ全体を一つの大きなJavaScriptアプリケーションとして扱うのに対し、AstroはHTMLをデフォルトの出力として重視します。ウェブサイトのほとんどの部分は静的なHTMLとしてレンダリングされ、インタラクティブなUIコンポーネント(例えば、カルーセル、ナビゲーショントグル、インタラクティブなフォームなど)は、孤立した「島(Islands)」としてページに配置されます。

重要なのは、これらの「島」のJavaScriptは、そのコンポーネントが必要になったとき、あるいは指定された条件(例:ユーザーがスクロールして表示領域に入ったとき)を満たしたときに初めてロード・実行されるという点です。ページ全体のJavaScriptバンドルを一度にロードする必要がありません。静的な部分はJavaScriptの実行を伴わないため、非常に高速にレンダリングされます。

Astroは「JavaScript Zero by default」(デフォルトではJavaScriptゼロ)を目指しています。これは、あなたが特に指定しない限り、UIフレームワークのJavaScriptはクライアントに送信されないという意味です。Reactコンポーネントを配置しても、それがインタラクティブな要素を持たない純粋な表示目的のものであれば、AstroはそれをサーバーサイドでHTMLにレンダリングし、クライアントにはそのHTMLだけを送信します。ReactのJavaScriptライブラリ自体はクライアントにロードされません。これにより、ページの初期ロードパフォーマンスが劇的に向上します。

静的サイト生成 (SSG) とサーバーサイドレンダリング (SSR)

AstroはSSGを第一に考えて設計されています。ビルド時にすべてのページを静的なHTMLファイルとして生成するため、CDNから高速に配信できます。これは、ブログ記事やマーケティングページのような、コンテンツが頻繁に更新されないサイトに最適です。

しかし、AstroはSSRもサポートしています。APIからのデータ取得が必要なページや、ユーザー認証に基づいた動的なコンテンツを表示する必要があるページでは、リクエストごとにサーバーサイドでページをレンダリングできます。これにより、SSGのパフォーマンスとSSRの柔軟性の両方を、必要に応じて使い分けることが可能です。

Astroファイル (.astro)

Astroのページやレイアウトは .astro 拡張子のファイルで記述されます。これはHTMLによく似たテンプレート構文を持ちますが、JavaScript(またはTypeScript)のコードブロックを含めることができます。このコードブロックはサーバーサイドで実行され、データの取得やコンポーネントへのプロップスの受け渡しなどに使われます。

“`astro

// これはサーバーサイドで実行されるJavaScript/TypeScriptコードブロックです
const pageTitle = “My Astro Page”;
const items = [“Item 1”, “Item 2”, “Item 3”];





{pageTitle}

{pageTitle}

これはAstroのページです。

リスト

    {items.map((item) => (

  • {item}
  • ))}



“`

この .astro ファイルの中で、後述するようにReactコンポーネントをインポートして使用します。

Reactとは? UI構築のための強力なライブラリ

Reactは、Facebook(現Meta)によって開発されたJavaScriptライブラリで、ユーザーインターフェース(UI)を構築するために使われます。現在のウェブ開発において、最も人気があり広く採用されているライブラリの一つです。

コンポーネントベースのアプローチ

Reactの核となる思想は「コンポーネント」です。UIを再利用可能な独立した部品(コンポーネント)に分割して構築します。各コンポーネントは独自のロジック、状態、そしてレンダリングするUIをカプセル化します。これにより、複雑なUIも管理しやすくなり、コードの再利用性が高まります。

仮想DOM (Virtual DOM)

Reactは「仮想DOM」という概念を使用します。これは、実際のブラウザのDOM(Document Object Model)の軽量なコピーをJavaScriptオブジェクトとしてメモリ上に保持するものです。Reactはコンポーネントの状態が変化した際に、仮想DOM上で差分計算を行い、変更が必要な最小限の部分だけを実際のDOMに効率的に反映します。このメカニズムにより、DOM操作のパフォーマンスが向上し、開発者は状態の管理に集中しやすくなります。

JSX

Reactは通常、JSX (JavaScript XML) という構文拡張を使用してコンポーネントのUI構造を記述します。JSXはJavaScriptコードの中にHTMLのようなタグを書けるようにするもので、UIの構造を直感的に表現できます。

“`jsx
// Reactコンポーネントの例
import React, { useState } from ‘react’;

function Counter() {
const [count, setCount] = useState(0);

return (

Count: {count}

);
}

export default Counter;
“`

広範なエコシステム

Reactの人気の理由の一つは、その巨大で活発なエコシステムです。状態管理ライブラリ(Redux, Zustand, Jotai)、ルーティングライブラリ(React Router)、UIコンポーネントライブラリ(Material UI, Ant Design, Chakra UI)、テストツール(Jest, React Testing Library)、サーバーサイドレンダリングフレームワーク(Next.js, Remix – これらはAstroとは異なるアプローチ)、開発者ツールなど、豊富なツールやライブラリが利用可能です。

なぜAstroでReactを使うのか? 最大のメリット

AstroとReact、それぞれに強力な特徴があることがわかりました。では、なぜこれらを組み合わせて使うのでしょうか?その最大のメリットは、それぞれの長所を最大限に引き出し、短所を補い合う点にあります。

  1. 既存のReactコンポーネント資産の活用: あなたやあなたのチームがすでに開発したReactコンポーネント、あるいはnpmなどで公開されているReactコンポーネントライブラリをAstroプロジェクトでそのまま(あるいは最小限の変更で)利用できます。ゼロからコンポーネントを作り直す必要がありません。
  2. 高いパフォーマンスと開発効率の両立: AstroのSSG/SSRとIsland Architectureによるパフォーマンスメリット(超高速な初期ロード、少ないJavaScript)を享受しながら、Reactの開発効率(コンポーネント指向、宣言的なUI記述、豊富なエコシステム)を活用できます。静的なコンテンツが多いページはAstroが得意な方法で高速に配信し、リッチなインタラクティブ性が必要な部分だけをReactコンポーネントで担当させる、という理想的な分業が可能です。
  3. アイランドアーキテクチャによるクライアント側JavaScriptの削減: Reactコンポーネントをページに配置しても、デフォルトではサーバーサイドでHTMLとしてレンダリングされます。インタラクティブにするために client: ディレクティブを使う場合でも、そのコンポーネントとその依存関係のJavaScriptのみがロード・実行されます。SPAのようにページ全体のJavaScriptをロードする必要がないため、ロード時間が短縮され、ユーザーデバイスのリソース消費も抑えられます。
  4. 部分的なインタラクティブ性の追加: ウェブサイト全体をSPAにする必要はありません。ブログ記事の中に「いいね」ボタンやコメントフォームといった特定のインタラクティブ要素だけをReactで実装したり、製品ページに動的な画像ギャラリーや設定オプションピッカーをReactで追加したりといったことが容易にできます。これにより、静的なページの高速性を保ちつつ、必要な箇所にだけリッチなUI/UXを提供できます。
  5. サーバーサイドでのレンダリング(SSG/SSR): Astroはビルド時またはリクエスト時にReactコンポーネントをサーバーサイドでHTMLにレンダリングします。これにより、FCP (First Contentful Paint) や LCP (Largest Contentful Paint) といったパフォーマンス指標が向上し、SEOにも有利になります。JavaScriptが無効になっているブラウザでも基本的なコンテンツが表示されるため、アクセシビリティも向上します。

要するに、AstroとReactの組み合わせは、「ほとんど静的だが部分的にインタラクティブな高性能ウェブサイト」を効率的に構築するための非常に強力な選択肢となります。

Astroプロジェクトのセットアップ

それでは、実際にAstroプロジェクトを作成し、Reactを使えるようにする手順を見ていきましょう。

前提条件

  • Node.js (v14.18.0, v16.12.0 以降) がインストールされていること。
  • npm, yarn, または pnpm のいずれかのパッケージマネージャーがインストールされていること。

プロジェクト作成コマンド

新しいAstroプロジェクトを作成するには、以下のコマンドをターミナルで実行します。

bash
npm create astro@latest

または

bash
yarn create astro

または

bash
pnpm create astro@latest

コマンドを実行すると、いくつかの質問に答える必要があります。

  1. Where would you like to create your new project? (プロジェクトを作成するディレクトリ名)
    • 例: my-astro-react-app
  2. How would you like to start your new project? (プロジェクトのテンプレート選択)
    • Just the basics (recommended) を選択するのがおすすめです。
  3. Include sample files? (サンプルファイルを含めるか)
    • Yes を選択すると、基本的なページ構成が作成されます。
  4. Install dependencies? (依存関係をインストールするか)
    • Yes を選択します。
  5. Do you plan to write TypeScript? (TypeScriptを使うか)
    • Reactと組み合わせる場合、TypeScriptを使うことが多いので Yes を選択するのがおすすめです。厳密な型チェックをしたい場合は Strict を選択します。
  6. Initialize a new git repository? (Gitリポジトリを初期化するか)
    • Yes を選択するのが一般的です。

質問に答えていくと、必要なファイルが作成され、依存関係がインストールされます。

プロジェクトディレクトリに移動します。

bash
cd my-astro-react-app

開発サーバーを起動してみましょう。

bash
npm run dev

または

bash
yarn dev

または

bash
pnpm dev

ブラウザで http://localhost:4321 にアクセスすると、Astroのデフォルトページが表示されるはずです。

プロジェクト構造の解説

デフォルトのプロジェクト構造は以下のようになります。

my-astro-react-app/
├── .gitattributes
├── .gitignore
├── .prettierignore
├── .prettierrc.cjs
├── README.md
├── astro.config.mjs
├── package.json
├── public/
│ └── favicon.svg
└── src/
├── components/ # UIコンポーネントを配置するディレクトリ
├── layouts/ # レイアウトコンポーネントを配置するディレクトリ
├── pages/ # 各ページのルートとなるファイル(ファイルベースルーティング)
│ └── index.astro
└── env.d.ts # TypeScriptの環境定義ファイル

  • src/pages/: このディレクトリ内の .astro ファイルは、ウェブサイトのページとして扱われます。例えば、src/pages/about.astro/about パスに対応します。index.astro はルートパス / に対応します。
  • src/layouts/: 複数のページで共通して使用するHTML構造(ヘッダー、フッター、ナビゲーションなど)を定義するためのレイアウトファイルです。
  • src/components/: Astroコンポーネント (.astro) や、後述するReactコンポーネント (.jsx, .tsx) などのUIコンポーネントを配置します。
  • public/: 静的なファイル(画像、フォント、robots.txt など)を配置します。これらのファイルはそのままルートパスでアクセス可能になります。
  • astro.config.mjs: Astroの設定ファイルです。インテグレーションの追加やビルド設定などを行います。
  • package.json: プロジェクトの依存関係やスクリプトが定義されます。

AstroへのReactインテグレーションの追加

AstroプロジェクトでReactコンポーネントを使うためには、Reactインテグレーションを追加する必要があります。Astro CLIを使えば、この作業は非常に簡単です。

プロジェクトのルートディレクトリで、以下のコマンドを実行します。

bash
npx astro add react

または

bash
yarn astro add react

または

bash
pnpm astro add react

このコマンドを実行すると、Astro CLIがReactインテグレーションのインストールと設定を自動で行ってくれます。

  1. 確認メッセージ: インテグレーションの追加を確認するメッセージが表示されます。Yes を選択して進めます。
  2. 依存関係のインストール: reactreact-dom@astrojs/react といった必要なパッケージが package.json に追加され、インストールされます。
  3. astro.config.mjs の更新: Astroの設定ファイルに @astrojs/react インテグレーションが自動的に追加されます。

インストールが完了すると、以下のようなメッセージが表示されるはずです。

✔ react installed.
✔ Added `@astrojs/react` integration to astro.config.mjs

これで、あなたのAstroプロジェクトでReactコンポーネントを使用する準備が整いました。

astro.config.mjs ファイルを開いてみると、以下のようになっているはずです(他の設定がある場合もあります)。

“`javascript
import { defineConfig } from ‘astro/config’;
import react from ‘@astrojs/react’; // この行が追加される

// https://astro.build/config
export default defineConfig({
integrations: [react()], // この行が追加される
});
“`

integrations 配列に react() が追加されていることがわかります。これにより、Astroは .jsx.tsx ファイルをReactコンポーネントとして認識し、適切に処理できるようになります。

基本的なReactコンポーネントの作成と表示

Reactインテグレーションを追加したので、早速Reactコンポーネントを作成し、Astroページに表示してみましょう。

Reactコンポーネントの作成

src/components/ ディレクトリ内に、新しいファイルを作成します。例えば、src/components/MyReactComponent.jsx または src/components/MyReactComponent.tsx という名前で作成します。ここではシンプルな静的コンポーネントを作成します。

“`jsx
// src/components/MyReactComponent.jsx

import React from ‘react’;

function MyReactComponent() {
return (

Hello from React!

This component was rendered by Astro.

);
}

export default MyReactComponent;
“`

TypeScriptを使用している場合は、以下のように記述します。

“`tsx
// src/components/MyReactComponent.tsx

import React from ‘react’;

const MyReactComponent: React.FC = () => {
return (

Hello from React! (TypeScript)

This component was rendered by Astro.

);
};

export default MyReactComponent;
“`

ここでは、状態を持たず、イベントハンドラも持たない、純粋な表示用のコンポーネントを作成しました。

AstroファイルでのReactコンポーネントのインポートと使用

次に、作成したReactコンポーネントをAstroページ (.astro ファイル) で使用します。src/pages/index.astro を編集してみましょう。

“`astro

import MyReactComponent from ‘../components/MyReactComponent’; // Reactコンポーネントをインポート
import Layout from ‘../layouts/Layout.astro’; // もしレイアウトを使っているならインポート




Welcome to Astro and React!

This is an Astro page.

{/* Reactコンポーネントを使用 */}

The component above was rendered using React.

“`

ポイントは以下の通りです。

  • --- で囲まれたフロントマターの中で、標準的なESモジュールの構文 (import) を使ってReactコンポーネントをインポートします。パスはAstroファイルからの相対パスまたはエイリアス(設定していれば)で指定します。
  • HTMLテンプレート部分で、カスタム要素のようにコンポーネントタグ <MyReactComponent /> を記述します。

開発サーバーが起動していれば(npm run dev)、ブラウザをリロードすると、作成したReactコンポーネントの内容がHTMLとして表示されていることが確認できます。

重要な点: この時点では、<MyReactComponent /> はサーバーサイドでHTMLにレンダリングされています。AstroはReactコンポーネントを静的に分析し、生成されるHTMLにその出力を含めます。クライアントサイドにReactのJavaScriptライブラリはロードされていません。 これはAstroの「JavaScript Zero by default」の原則に基づいたデフォルトの挙動です。純粋な表示コンポーネントの場合、これで十分であり、パフォーマンス上の大きなメリットとなります。

インタラクティブなReactコンポーネントの使用(クライアント側レンダリング)

Reactコンポーネントの真価は、状態を持ち、ユーザーインタラクションに応じて変化する「インタラクティブ性」にあります。AstroでReactコンポーネントをインタラクティブにするためには、client: ディレクティブ を使用します。これは、AstroのIsland Architectureにおいて、特定のコンポーネントをクライアントサイドでハイドレーション(サーバーレンダリングされたHTMLにJavaScriptのイベントハンドラや状態管理を紐づける処理)させるための指示です。

client: ディレクティブの種類

Astroは、コンポーネントをハイドレーションするタイミングを制御するための様々な client: ディレクティブを提供します。これにより、必要なJavaScriptだけを、必要なタイミングでロード・実行できます。

  • client:load: ページがロードされ、DOMがインタラクティブになった直後にコンポーネントをハイドレーションします。最も一般的でシンプルなオプションです。コンポーネントがページの上部にある場合や、すぐにインタラクティブになる必要がある場合 に適しています。
  • client:idle: ブラウザの requestIdleCallback APIを使用して、ブラウザのアイドル時間中にコンポーネントをハイドレーションします。ページの初期ロードを妨げないため、重要度の低いインタラクティブ要素 に適しています。
  • client:visible: ブラウザの Intersection Observer API を使用して、コンポーネントがビューポート(画面表示領域)に入ったときにハイドレーションを開始します。これにより、スクロールしないと見えない、ページのより下部にあるコンポーネント のJavaScriptのロードを遅延させることができます。client:visible={{ rootMargin: '200px' }} のようにオプションを渡すことも可能です(例:ビューポートに入る200px手前でロードを開始)。
  • client:media={QUERY}: 指定したCSSメディアクエリに一致した場合にハイドレーションを開始します。例えば、client:media="(max-width: 600px)" とすれば、モバイルデバイスで表示されたときだけコンポーネントがインタラクティブになります。特定の画面サイズでのみインタラクティブ性が必要なコンポーネント に適しています。
  • client:only={FRAMEWORK}: このディレクティブは少し特殊です。サーバーサイドレンダリングを行わず、クライアントサイドでのみコンポーネリングをレンダリング します。client:only="react" のようにフレームワーク名を指定します。これは、ブラウザAPIに依存するコンポーネント(例:window オブジェクトを使うもの)や、サーバーレンダリングが難しいライブラリ(例:Styled Componentsなど、特定のCSS-in-JSライブラリの一部)を使う場合に必要になることがあります。ただし、サーバーレンダリングされないため、初期ロード時のパフォーマンスやSEOには不利になる可能性があります。可能な限り他の client: ディレクティブを使用することが推奨されます。

インタラクティブコンポーネントの実装例

先ほど作成した MyReactComponent を、クリックするとカウンターが増えるインタラクティブなコンポーネントに変更してみましょう。

“`jsx
// src/components/MyInteractiveReactComponent.jsx

import React, { useState } from ‘react’; // useStateをインポート

function MyInteractiveReactComponent() {
const [count, setCount] = useState(0); // 状態を定義

const handleClick = () => { // イベントハンドラを定義
setCount(count + 1);
console.log(‘Button clicked!’);
};

return (

Interactive React Component

Current count: {count}

(This component is interactive thanks to a client directive)

);
}

export default MyInteractiveReactComponent;
“`

このコンポーネントは useState Hookを使用して内部状態 (count) を管理し、ボタンの onClick イベントハンドラでその状態を更新します。これはインタラクティブな動作を含むため、クライアントサイドでReactによって実行される必要があります。

次に、この新しいコンポーネントをAstroページで使用します。ここでは例として client:load ディレクティブを使ってみましょう。

“`astro

import MyReactComponent from ‘../components/MyReactComponent’; // 静的な方
import MyInteractiveReactComponent from ‘../components/MyInteractiveReactComponent’; // インタラクティブな方
import Layout from ‘../layouts/Layout.astro’;




Welcome to Astro and React!

This is an Astro page.

Static Component (Server Rendered)

This component was rendered by Astro on the server. No client-side JavaScript for this part.


Interactive Component (Client Hydrated)

{/* client:load を付けてコンポーネントをハイドレーション */}

This component is interactive. It is hydrated on the client after the page loads.


Another Interactive Component (Client Visible)

{/* client:visible を付けてコンポーネントが表示されたらハイドレーション */}

{/* スクロールしないと見えないようにするための余白 */}

This component will be hydrated only when it enters the viewport.

“`

開発サーバーを起動または再起動してブラウザで確認してください。

  • 最初の静的なコンポーネントはすぐに表示され、ボタンはありません。
  • client:load を付けたインタラクティブコンポーネントもページロード時にはHTMLとして表示されていますが、ロードが完了するとハイドレーションが行われ、ボタンがクリック可能になります。クリックするとカウンターが増えるはずです。
  • client:visible を付けたインタラクティブコンポーネントは、最初は静的なHTMLとして表示されていますが、ページをスクロールしてそのコンポーネントが画面に入ってくると、JavaScriptがロードされてハイドレーションされ、ボタンがクリック可能になります。

このように、Astroでは client: ディレクティブを使って、どのReactコンポーネントを、いつクライアントサイドでインタラクティブにするかを細かく制御できます。これがIsland Architectureの力であり、必要な部分にだけJavaScriptのコストを支払うことを可能にします。

client:only ディレクティブについて補足

先述の client:only="react" ディレクティブは、サーバーレンダリングを行わないため、コンポーネントの初期表示時に遅延が発生する可能性があります(JavaScriptがロード・実行されてからDOMが生成されるため)。また、HTMLソースにはそのコンポーネントの出力が含まれないため、SEOにも影響を与える可能性があります。

しかし、ブラウザAPI(例: window, localStorage など)に直接アクセスするコンポーネントや、特定のクライアントサイド専用ライブラリ(例: 一部の認証ライブラリ、canvas を使う描画ライブラリなど)を使う場合には、client:only が必要になることがあります。Styled ComponentsやEmotionなどの一部のCSS-in-JSライブラリも、サーバーレンダリングを適切に設定しない場合や、ランタイムCSS生成に依存する機能を使う場合に client:only を必要とすることがあります。

例:

“`astro

import ClientOnlyComponent from ‘../components/ClientOnlyComponent’;



Client Only Component

This component is rendered only on the client.


“`

可能な限り他の client: ディレクティブを使うように心がけ、client:only は最終手段として利用するのがAstroのパフォーマンスメリットを最大限に活かすための推奨アプローチです。

React Hooksと状態管理

Reactコンポーネントがインタラクティブになるということは、状態を管理し、ユーザーインタラクションに応じてUIを更新できるということです。AstroでReactコンポーネントを使用する場合でも、標準的なReact Hooks(useState, useEffect, useContext, useReducer など)を完全に利用できます。

基本的なHooksの使用

先ほどのカウンターの例のように、useState を使ってコンポーネントのローカル状態を管理できます。

“`jsx
import React, { useState, useEffect } from ‘react’;

function Timer() {
const [seconds, setSeconds] = useState(0);

useEffect(() => {
// コンポーネントがマウントされた後にタイマーを開始
const intervalId = setInterval(() => {
setSeconds(prevSeconds => prevSeconds + 1);
}, 1000);

// クリーンアップ関数:コンポーネントがアンマウントされるときにタイマーをクリア
return () => clearInterval(intervalId);

}, []); // 空の依存配列はマウント時とアンマウント時にのみ実行されることを意味する

return (

Timer (using useState and useEffect)

Elapsed seconds: {seconds}

);
}

export default Timer;
“`

この Timer コンポーネントをAstroページで使用するには、やはり client: ディレクティブが必要です。

“`astro

import Timer from ‘../components/Timer’;



Using React Hooks in Astro



“`

コンポーネント間での状態共有

より複雑なアプリケーションでは、複数のReactコンポーネント間で状態を共有する必要があります。Astroのページは基本的に独立した「島」の集まりですが、同じページ内のReactコンポーネント間であれば、標準的なReactの状態管理手法を利用できます。

  • Propsのバケツリレー: 親コンポーネントから子コンポーネントへPropsとして状態や更新関数を渡す。シンプルですが、コンポーネントツリーが深くなると煩雑になります。
  • Context API: Reactに組み込まれている機能で、コンポーネントツリーを深く辿ることなく状態を共有できます。小規模から中規模のアプリケーションでよく使われます。Context Providerを、状態を共有したい子コンポーネント群を囲むように配置する必要があります。Astroページ内でContext Providerをラップコンポーネントとして使用できます。

    “`jsx
    // src/components/ThemeContext.jsx
    import React, { createContext, useContext, useState } from ‘react’;

    const ThemeContext = createContext();

    export const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState(‘light’);
    const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === ‘light’ ? ‘dark’ : ‘light’));
    };

    return (

    {children}

    );
    };

    export const useTheme = () => useContext(ThemeContext);
    “`

    “`jsx
    // src/components/ThemeToggleButton.jsx
    import React from ‘react’;
    import { useTheme } from ‘./ThemeContext’;

    const ThemeToggleButton = () => {
    const { theme, toggleTheme } = useTheme();
    return (

    );
    };

    export default ThemeToggleButton;
    “`

    “`jsx
    // src/components/ThemeDisplay.jsx
    import React from ‘react’;
    import { useTheme } from ‘./ThemeContext’;

    const ThemeDisplay = () => {
    const { theme } = useTheme();
    return

    Current theme: {theme}

    ;
    };

    export default ThemeDisplay;
    “`

    “`astro

    import Layout from ‘../layouts/Layout.astro’;
    // ThemeProviderは他のContextコンポーネントをラップするので、client:loadが必要
    import { ThemeProvider } from ‘../components/ThemeContext’;
    import ThemeToggleButton from ‘../components/ThemeToggleButton’;
    import ThemeDisplay from ‘../components/ThemeDisplay’;



    Using React Context API in Astro

    {/* ThemeProviderで子コンポーネントを囲む */}
    <ThemeProvider client:load>
      <div>
        <ThemeDisplay />
        <ThemeToggleButton />
      </div>
    </ThemeProvider>
    



    “`

  • 状態管理ライブラリ (Redux, Zustand, Jotaiなど): より大規模なアプリケーションや複雑な状態管理には、Redux、Zustand、Jotaiといった専門的な状態管理ライブラリを導入することも可能です。これらのライブラリは通常、アプリケーション全体または特定のサブツリーにProviderコンポーネントを配置したり、カスタムHookを使って状態にアクセスしたりするパターンを取ります。Astroで使用する場合、Providerコンポーネントには client: ディレクティブ(通常は client:load または client:only)を付ける必要があり、そのProviderでラップされたReactコンポーネントはその状態管理ライブラリの機能を利用できるようになります。

    注意点: これらの状態管理ライブラリは、通常、状態管理ロジックやStore全体がクライアントサイドJavaScriptとしてロードされるため、Island Architectureの粒度よりも大きな範囲に影響を与える可能性があります。ライブラリを選択・使用する際は、この点を理解しておくことが重要です。

PropsとしてAstroからReactへデータを渡す

Astroのフロントマターで取得したデータや、Astroコンポーネント内で定義した変数を、PropsとしてReactコンポーネントに渡すことができます。

“`astro

import MyReactComponent from ‘../components/MyReactComponent’;

const greeting = “Hello”;
const name = “Astro User”;
const itemCount = 5;
const items = [
{ id: 1, text: ‘Apple’ },
{ id: 2, text: ‘Banana’ },
{ id: 3, text: ‘Cherry’ },
];




Passing Data to React Components

{/* 文字列や数値などのプリミティブ値を渡す */}

{/* 配列やオブジェクトを渡す */}

{/* リアクティブなコンポーネントにデータを渡し、それが状態の初期値になる場合 */}


“`

“`jsx
// src/components/MyReactComponent.jsx (Propsを受け取る側)
import React from ‘react’;

function MyReactComponent(props) {
// props オブジェクトから渡されたデータにアクセス
console.log(“Props received:”, props); // デバッグ用

return (

Component with Props

{props.greeting && props.userName &&

{props.greeting}, {props.userName}!

}
{props.items && (

    {props.items.map(item => (

  • {item.text}
  • ))}

)}
{!props.greeting && !props.items &&

No specific props passed.

}

);
}

export default MyReactComponent;
“`

“`jsx
// src/components/MyInteractiveReactComponent.jsx (Propsを初期値として使う側)
import React, { useState } from ‘react’;

function MyInteractiveReactComponent({ initialCount = 0 }) {
// PropsをuseStateの初期値として使う
const [count, setCount] = useState(initialCount);

const handleClick = () => {
setCount(count + 1);
};

return (

Interactive Component with Initial Prop

Initial count was: {initialCount}

Current count: {count}

);
}

export default MyInteractiveReactComponent;
“`

Propsとして渡されたデータは、サーバーサイドでのレンダリング時にReactコンポーネントに渡され、初期HTMLに反映されます。client: ディレクティブを持つコンポーネントの場合、ハイドレーション時にも同じPropsが渡され、Reactがコンポーネントの状態を初期化するために使用します。

ただし、Astroのフロントマターの変数は、クライアントサイドのJavaScriptからは直接アクセスできません。サーバーサイドで実行されるコードと、クライアントサイドで実行されるReactコードは分離しているということを理解しておく必要があります。Propsは、このサーバーサイドとクライアントサイドの間のデータの橋渡しとして機能します。

スタイリング

AstroプロジェクトでReactコンポーネントを含むUIをスタイリングする方法はいくつかあります。Astro自身のスタイリング機能と、Reactの世界で一般的に使われるスタイリング手法を組み合わせて使用できます。

  1. Astroの <style> タグ: .astro ファイル内で <style> タグを使用すると、そのページまたはコンポーネントにスタイルを適用できます。デフォルトでは、 <style> タグのスタイルはカプセル化(Scoped CSS)されます。グローバルスタイルには <style is:global> を使用します。この方法は、Reactコンポーネントの外側の要素や、Reactコンポーネント自体に影響する(例:パディングやマージンなど)スタイルに便利です。

    “`astro

    “`

  2. グローバルCSSファイル: 標準的な .css ファイルを作成し、Astroのレイアウトファイルやページでインポートする方法です。グローバルに適用したいスタイルや、Tailwind CSSのようなユーティリティファーストなCSSフレームワークを使う場合に適しています。

    css
    /* src/styles/global.css */
    body {
    background-color: #f4f4f4;
    }

    “`astro

    import ‘../styles/global.css’;







    “`

  3. CSSモジュール (CSS Modules): Reactの世界で広く使われている手法で、CSSクラス名を自動的にユニークなものに変更することで、スタイル間の競合を防ぎます。Reactコンポーネントごとに .module.css ファイルを作成し、それをインポートして使用します。AstroはReactコンポーネント内のCSSモジュールを自動的にサポートします。

    “`css
    / src/components/MyReactComponent.module.css /
    .container {
    border: 2px solid teal;
    padding: 1em;
    margin: 1em 0;
    }

    .title {
    color: darkcyan;
    font-size: 1.5em;
    }
    “`

    “`jsx
    // src/components/MyReactComponent.jsx
    import React from ‘react’;
    import styles from ‘./MyReactComponent.module.css’; // CSSモジュールをインポート

    function MyReactComponent() {
    return (

    {/ インポートしたスタイルを適用 /}

    Hello from React!

    This component uses CSS Modules for styling.

    );
    }

    export default MyReactComponent;
    “`

  4. CSS-in-JSライブラリ (Styled Components, Emotionなど): JavaScriptコード内でスタイルを記述する方法です。Reactコンポーネントと密接に連携するため、インタラクティブなスタイル変更などを容易に実装できます。ただし、これらのライブラリはクライアントサイドでのランタイムが基本的に必要となるため、Astroで使用するReactコンポーネントにはclient:only ディレクティブが必要になることが多いです。サーバーサイドレンダリング(SSR)をサポートしているライブラリでも、Astroのアイランドアーキテクチャ内で適切に設定するには追加の作業が必要になる場合があります。パフォーマンスの観点からは、CSSモジュールや標準CSSが推奨されることが多いです。

    “`jsx
    // src/components/StyledButton.jsx
    import React from ‘react’;
    import styled from ‘styled-components’; // 例としてStyled Components

    const Button = styled.button`
    background-color: palevioletred;
    color: white;
    font-size: 1em;
    margin: 1em;
    padding: 0.25em 1em;
    border: 2px solid palevioletred;
    border-radius: 3px;
    cursor: pointer;

    &:hover {
    background-color: white;
    color: palevioletred;
    }
    `;

    const StyledButton = () => {
    return ;
    };

    export default StyledButton;
    “`

    “`astro

    // StyledButton を使うには client:only が必要になる可能性が高い
    import StyledButton from ‘../components/StyledButton’;



    Styled Components in Astro

    {/ Styled Componentsを使うコンポーネントには client:only /}



    ``
    Styled ComponentsやEmotionをSSRで使うには、Astroの設定やProviderの追加など、フレームワーク固有のセットアップが必要になります。これは少し高度なトピックなので、ここでは
    client:only` での利用例に留めますが、よりパフォーマンスが重要な場合は、他のスタイリング手法を検討するか、ライブラリのSSR設定について深く調べる必要があります。

推奨されるアプローチ:

  • ページ全体のレイアウトや、Reactコンポーネントに直接関係しない静的要素のスタイリングには、Astroの <style> タグやグローバルCSSを利用する。
  • 個々のReactコンポーネント内のスタイリングには、CSSモジュールを利用する。これがAstroのパフォーマンスメリットとも相性が良く、スタイルの競合も防げるため、Reactとの組み合わせでは最も推奨される手法の一つです。
  • CSS-in-JSライブラリを使う場合は、client:only が必要になる可能性が高いことを理解し、そのパフォーマンス上のトレードオフを受け入れるか、SSR設定を慎重に行う。

データの取得

ウェブサイト開発において、外部APIからのデータ取得やローカルデータの読み込みは一般的です。AstroとReactを組み合わせたプロジェクトでは、データを取得するタイミングや場所によっていくつかの方法があります。

1. Astro側でのデータ取得(ビルド時 または サーバーサイド)

Astroのフロントマター(--- ブロック)で実行されるコードは、デフォルトではビルド時(SSGの場合)またはリクエスト時(SSRの場合)にサーバーサイドで実行されます。ここでデータを取得し、それをPropsとしてReactコンポーネントに渡すのが、Astroのパフォーマンスを最大限に活かす推奨パターンです。

これにより、ページのHTMLが生成される時点でデータが埋め込まれるため、クライアントサイドでのJavaScriptによるデータ取得・表示待ちが発生しません。

“`astro

import Layout from ‘../layouts/Layout.astro’;
import PostList from ‘../components/PostList’; // データを表示するReactコンポーネント

// サーバーサイドで実行されるデータ取得ロジック
// SSGの場合はビルド時に1回、SSRの場合はリクエストごとに実行
async function fetchPosts() {
const response = await fetch(‘https://jsonplaceholder.typicode.com/posts?_limit=5’);
const posts = await response.json();
return posts;
}

const posts = await fetchPosts(); // データを取得



Posts (Fetched Server-Side)

{/* 取得したデータをReactコンポーネントにPropsとして渡す */}
{/* このPostListは静的な表示のみであれば client:ディレクティブは不要 */}

Data was fetched on the server before the page was sent to the browser.


“`

“`jsx
// src/components/PostList.jsx
import React from ‘react’;

const PostList = ({ posts }) => {
// プロップスとして受け取ったpostsを表示
return (

    {posts.map(post => (

  • {post.title}

    {post.body.substring(0, 100)}…

  • ))}

);
};

export default PostList;
“`

この例では、PostList コンポーネントは受け取ったデータを表示するだけで、特にインタラクティブな要素はありません。したがって、client: ディレクティブは不要です。もし、このリストに「いいね」ボタンのようなインタラクティブな要素が含まれる場合は、PostList またはその子コンポーネントに client: ディレクティブが必要になります。

2. クライアント側でのデータ取得(Reactコンポーネント内)

特定のReactコンポーネントが、ユーザーのアクションに応じて動的にデータを取得する必要がある場合や、コンポーネントがマウントされた後にしか利用できない情報(例:ブラウザのGeolocation APIなど)に依存する場合、Reactコンポーネント内でデータを取得します。これは、useEffect Hookを使用して行われるのが一般的です。

注意点: この方法は、ページの初期ロード時にはデータが表示されず、コンポーネントがクライアントサイドでハイドレーションされ、JavaScriptが実行されてからデータ取得・表示が行われます。ページの初期表示速度やSEOにとっては不利になる可能性があります。したがって、この方法は必要な場面に限定して使用することが推奨されます。

“`jsx
// src/components/DynamicDataFetcher.jsx
import React, { useState, useEffect } from ‘react’;

const DynamicDataFetcher = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
// コンポーネントがマウントされた後にクライアントサイドでデータを取得
const fetchData = async () => {
try {
const response = await fetch(‘https://api.example.com/dynamic-data’); // クライアントサイドAPI
if (!response.ok) {
throw new Error(HTTP error! status: ${response.status});
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};

fetchData();

}, []); // 空の依存配列でマウント時に一度だけ実行

if (loading) {
return

Loading dynamic data…

;
}

if (error) {
return

Error fetching data: {error.message}

;
}

return (

Dynamic Data (Fetched Client-Side)

{JSON.stringify(data, null, 2)}

);
};

export default DynamicDataFetcher;
“`

このコンポーネントをAstroページで使用する場合、データ取得ロジックがクライアントサイドで実行されるため、必ず client: ディレクティブが必要です。

“`astro

import Layout from ‘../layouts/Layout.astro’;
import DynamicDataFetcher from ‘../components/DynamicDataFetcher’;




Client-Side Data Fetching in React

{/* このコンポーネントは client:load でハイドレーションされ、内部でデータをフェッチ */}

Data for the component above is fetched in the browser after the page loads.


“`

この例では client:load を使っていますが、コンポーネントがビューポートに入るまでデータ取得を遅延させたい場合は client:visible を使うことも考えられます。

どちらの方法を選ぶべきか?

  • Astro側(サーバーサイド/ビルド時)での取得:

    • メリット: 高速な初期表示、SEOに強い、JavaScript無効環境でもコンテンツが表示される。
    • デメリット: ユーザーインタラクションによる動的な再フェッチや、ブラウザ専用APIに依存するデータ取得には不向き。
    • 使用シーン: コンテンツがページのロード時に確定している場合、ブログ記事一覧、製品リスト、プロフィールの表示など。
  • React側(クライアントサイド)での取得:

    • メリット: ユーザーインタラクションやブラウザ環境に応じた動的なデータ取得、ブラウザ専用APIの利用。
    • デメリット: 初期表示の遅延、SEOへの影響(クローラーがJavaScriptを実行しない場合)、JavaScript必須。
    • 使用シーン: ユーザー検索結果のリアルタイム表示、認証後でないと取得できないデータ、位置情報を使った機能など、特定のインタラクションやクライアント環境に依存する場合。

Astroの philosophy に沿うなら、可能な限りAstro側でデータを取得し、それをPropsとしてReactコンポーネントに渡すのがベストです。クライアントサイドでのデータ取得は、やむを得ない場合や、明確な理由(例:ユーザーのアクション後など)がある場合に留めるのが望ましいです。

TypeScriptの利用

AstroもReactもTypeScriptを強くサポートしています。プロジェクト作成時にTypeScriptを選択していれば、基本的な設定は完了しています。Reactコンポーネントを .tsx ファイルで記述し、型定義を追加することで、開発中のエラーを減らし、コードの保守性を向上させることができます。

astro add react コマンドは、必要に応じてReact関連のTypeScript設定も自動で行います。

.tsx ファイルの作成と型定義

Reactコンポーネントを .tsx ファイルで作成し、Propsや状態に型を付けます。

“`tsx
// src/components/Greeting.tsx

import React from ‘react’;

// Propsの型を定義
interface GreetingProps {
name: string;
age?: number; // ageはオプション
}

const Greeting: React.FC = ({ name, age }) => {
return (

Hello, {name}!

{age !== undefined &&

You are {age} years old.

}

);
};

export default Greeting;
“`

Astroファイルでこのコンポーネントを使用する際も、TypeScriptの恩恵を受けられます。Propsの型が定義されているため、誤った型の値を渡したり、必須のPropsを渡し忘れたりすると、エディタやビルド時に警告やエラーが表示されます。

“`astro

import Layout from ‘../layouts/Layout.astro’;
import Greeting from ‘../components/Greeting’; // .tsx ファイルをインポート




Using TypeScript with React in Astro

{/* Propsを正しく渡す */}

{/* 必須のPropsだけ渡す */}

{/* 型エラーが発生する例 (エディタやビルドで警告/エラー) */}
{/* */}
{/* // nameが必須なのに渡していない */}


“`

Astroファイル自体もTypeScriptで記述できます。フロントマターでTypeScriptの構文を使用し、変数の型を宣言することが可能です。

“`astro

// Astroファイル内でのTypeScript
interface PageProps {
title: string;
}

const { title = “Default Title” } = Astro.props as PageProps;

// Reactコンポーネントのインポート
import Greeting from ‘../components/Greeting’;

// 変数に型を付ける
const userName: string = “Charlie”;
const userAge: number | undefined = 25; // number型またはundefined




Astro Page with TypeScript

Page Title from props: {title}

{/* Astroの変数をPropsとして渡す */}


“`

AstroプロジェクトでTypeScriptを使うことで、AstroファイルとReactコンポーネントの両方で型安全性を享受でき、大規模なプロジェクトでの開発効率と信頼性が向上します。

パフォーマンスの最適化

AstroとReactの組み合わせはデフォルトでも高いパフォーマンスを提供しますが、さらに最適化を進めるための考慮事項がいくつかあります。

  1. client: ディレクティブの適切な選択: これが最も重要です。全てのReactコンポーネントに安易に client:load を付けるのではなく、コンポーネントの要件に合わせて最適なディレクティブを選択してください。

    • すぐにインタラクティブにする必要がない場合は client:idle
    • スクロールしないと見えない要素には client:visible
    • 特定のブレークポイントでのみインタラクティブにする場合は client:media
    • 本当にサーバーレンダリングが不可能でクライアント専用機能に依存する場合のみ client:only を検討する。
      これにより、クライアントに送信されるJavaScriptの量と、その実行タイミングを最適化できます。
  2. 大きなインタラクティブコンポーネントの分割: 非常に多くの機能や複雑な状態を持つ大きなReactコンポーネントがある場合、その全てを一度にハイドレーションするとコストが高くなります。可能であれば、大きなコンポーネントをより小さな、独立した「島」に分割することを検討してください。例えば、ショッピングカートページで、商品リスト表示部分は静的に、数量変更や削除ボタンを持つ部分は小さなインタラクティブコンポーネントとして分割するといったアプローチです。

  3. 画像の最適化: 画像はウェブサイトのパフォーマンスに大きな影響を与えます。Astroには公式の @astrojs/image インテグレーションがあり、画像のサイズ変更、フォーマット変換(WebPなど)、遅延読み込み(Lazy Loading)などを自動的に行えます。Reactコンポーネント内で画像を使用する場合でも、Astro Imageコンポーネントを活用することで、画像関連のパフォーマンスを大幅に向上させることができます。

    “`astro

    import { Image } from ‘@astrojs/image/components’; // Astro Imageコンポーネントをインポート
    import MyReactComponent from ‘../components/MyReactComponent’;
    import heroImage from ‘../assets/hero.png’; // ローカル画像をインポート



    Image Optimization Example

    {/* Astro Image コンポーネントの使用 */}
    <Image src={heroImage} width={600} alt="A hero image" />
    
    {/* React コンポーネント内で画像を使用する場合 */}
    {/* もしReactコンポーネントがclient: ディレクティブを持つなら、その内部で画像を扱う */}
    {/* Reactコンポーネント内で使う画像も最適化したい場合は、React対応の画像コンポーネントライブラリか、 */}
    {/* Astro Imageコンポーネントをpropsとして渡すなどを検討 */}
    



    ``
    Reactコンポーネント内でAstroの
    コンポーネントを直接使うことはできません。もしReactコンポーネント内で画像を最適化したい場合は、React専用の画像最適化ライブラリを使うか、Astro側で最適化された画像のURLをReactコンポーネントにPropsとして渡すなどの方法を検討します。@astrojs/imageはReact用の` コンポーネントも提供しています。

  4. コード分割 (Code Splitting): Astroはデフォルトでコード分割を賢く行いますが、特に client:only を使用している場合や、大きな状態管理ライブラリを使用している場合などは、生成されるJavaScriptバンドルのサイズを確認することが重要です。AstroやVite(Astroのビルドツール)の設定で、チャンク分割をさらに細かく制御することも可能です。動的インポート (import()) は、必要なときにだけコードをロードするのに役立ちますが、Reactコンポーネント内でこれを行う場合は、React.lazy や Suspense と組み合わせて使用することになります。

  5. 不要なPropの削減: AstroからReactコンポーネントに渡すPropが多い場合、サーバーレンダリングされたHTMLにそのデータが埋め込まれる(シリアライズされる)量が増えます。特に大きなデータ構造(例えば、APIから取得した全データの巨大な配列)をPropsとして渡すと、HTMLサイズが増加し、ハイドレーションのコストも高くなる可能性があります。Reactコンポーネントが必要とする最小限のデータのみをPropsとして渡すように心がけましょう。

サーバーサイドレンダリング (SSR) とReact

AstroはデフォルトでSSGに最適化されていますが、アダプターを追加することでSSRも有効にできます。SSRモードでは、ページへのリクエストがあるたびにサーバーサイドでページがレンダリングされ、クライアントにHTMLが送信されます。Reactコンポーネントも、SSG時と同様にサーバーサイドでHTMLとしてレンダリングされます。

SSRモードでの client: ディレクティブの挙動はSSGモードとほぼ同じですが、サーバーサイドで最初のHTMLが生成されるという点が異なります。

SSRの有効化

AstroプロジェクトでSSRを有効にするには、使用するデプロイ環境(Node.jsサーバー、Vercel、Netlifyなど)に応じたアダプターをインストールし、astro.config.mjs に追加します。

例:Node.jsアダプターを使用する場合

bash
npx astro add node

または

bash
yarn astro add node

または

bash
pnpm astro add node

コマンドを実行すると、astro.config.mjs が更新されます。

“`javascript
import { defineConfig } from ‘astro/config’;
import react from ‘@astrojs/react’;
import node from ‘@astrojs/node’; // この行が追加される

// https://astro.build/config
export default defineConfig({
integrations: [react()],
output: ‘server’, // この行が追加され、SSRが有効になる
adapter: node({
mode: ‘standalone’ // または ‘middleware’
})
});
“`

output: 'server' を設定することで、ビルド時の出力が静的ファイルではなく、サーバーサイドで実行されるコードに変わります。

SSRとハイドレーション

SSRモードでも、Reactコンポーネントに client: ディレクティブが付いている場合、サーバーサイドでレンダリングされたHTMLに対してクライアントサイドでハイドレーションが行われます。これにより、ユーザーはすぐにコンテンツを見ることができ、その後コンポーネントがインタラクティブになります。

SSRとクライアント側レンダリングの使い分け

  • SSR: ページのコンテンツがリクエストごとに動的に変わる場合(例:認証されたユーザー向けの内容、APIからのリアルタイムデータ)。初期表示が速く、SEOに有利。
  • クライアント側レンダリング(client:only を含む): SSRでは扱えないクライアント固有の機能が必要な場合、あるいは初期表示がそれほど重要でない部分。

AstroのSSRは、SSGのパフォーマンスメリットを維持しつつ、動的なコンテンツへの対応力を高めるためのものです。Reactコンポーネントは、このSSRパイプラインの一部としてシームレスに動作します。

既存Reactプロジェクトからの移行

既存のReactアプリケーションをAstroに移行することを検討している場合、段階的なアプローチを取ることができます。

  1. Astroプロジェクトのセットアップ: 新しいAstroプロジェクトを作成し、Reactインテグレーションを追加します。
  2. レイアウトと静的ページの作成: Astroのレイアウト機能を使って、サイト共通のヘッダー、フッター、ナビゲーションなどを構築します。既存サイトの静的なコンテンツ(Aboutページ、Contactページなど)を .astro ファイルとして移行します。この段階では、Reactはまだ使用しないか、静的な表示目的のみで使用します。
  3. 既存Reactコンポーネントの移植: 既存のReactプロジェクトからUIコンポーネントのコード(.jsx/.tsx ファイル)をAstroプロジェクトの src/components ディレクトリなどにコピーします。依存しているライブラリ(UIライブラリ、状態管理ライブラリなど)も package.json に追加しインストールします。
  4. ページ単位での置き換え: 既存サイトのページを一つずつAstroページ(.astro ファイル)に置き換えていきます。新しいAstroページの中で、移植したReactコンポーネントをインポートし、client: ディレクティブを適切に使用してインタラクティブな部分を有効にします。
  5. ルーティングの移行: 既存Reactアプリがクライアントサイドルーター(React Routerなど)を使用している場合、Astroのファイルベースルーティングに移行します。React Routerで管理されていた各ルートをAstroの src/pages ディレクトリ内の対応するファイルに変換します。複雑な動的ルーティングがある場合は、Astroの動的ルーティング機能([slug].astro など)を利用します。React RouterでSPAとして実現していたルーティングは、Astroでは各ページが独立したHTMLとして扱われるため、クライアントサイドルーティングのロジックは不要になるか、特定の「島」の中で局所的に使用する形になります(通常は非推奨)。
  6. 状態管理とデータ取得の調整: グローバルな状態管理ライブラリを使っていた場合、それをAstroのIsland Architectureに合わせて調整する必要があります。状態を共有したい範囲に応じて、Context APIを使ったり、特定のReactコンポーネントツリー内でのみ状態管理ライブラリを使用したりします。可能なデータ取得はAstroのフロントマターに移行します。
  7. ビルドとデプロイ: Astroのビルド (npm run build) を実行し、生成された静的サイトまたはサーバーサイドバンドルをデプロイします。

この段階的なアプローチにより、リスクを抑えながら少しずつAstroのメリットを取り入れることができます。特に、コンテンツ配信のパフォーマンスが重要で、インタラクティブ性が限定的な部分に集中しているサイトでは、大きな改善が見込めます。

デプロイ

Astroプロジェクトのデプロイ方法は、SSGサイトかSSRサイトかによって異なります。

静的サイト (SSG) のデプロイ

output: 'static' (デフォルト) の場合、npm run build コマンドはプロジェクトのルートディレクトリに dist/ ディレクトリを生成します。このディレクトリには、HTML、CSS、アセット、そして client: ディレクティブを持つコンポーネントに必要なJavaScriptファイルが含まれます。

この dist/ ディレクトリの内容は、あらゆる静的ホスティングサービスにデプロイできます。主要なサービスには以下があります。

  • Netlify: dist/ ディレクトリを公開ディレクトリとして設定するだけです。Netlify Functionsを使えばSSRも可能です(Astroアダプターを利用)。
  • Vercel: 同様に dist/ ディレクトリを公開ディレクトリとして設定します。Vercel Functionsを使えばSSRも可能です(Astroアダプターを利用)。
  • GitHub Pages: dist/ ディレクトリを公開ソースとして設定します。
  • Cloudflare Pages: dist/ ディレクトリを公開ディレクトリとして設定します。Cloudflare Workersを使えばSSRも可能です(Astroアダプターを利用)。
  • AWS S3 & CloudFront: S3に dist/ の内容をアップロードし、CloudFrontで配信します。

ほとんどのCI/CDサービスは、ビルドコマンド (npm run build) を実行し、dist/ ディレクトリの内容を自動的にデプロイするように設定できます。

SSRサイトのデプロイ

output: 'server' の場合、npm run build は実行環境に応じたサーバーサイドコード(例:Node.jsサーバーのエントリーポイント、サーバーレス関数)を生成します。この出力は、標準的な静的ホスティングサービスには直接デプロイできません。

SSRサイトのデプロイには、以下のサービスがよく利用されます。

  • Vercel: @astrojs/vercel アダプターを使用します。AstroプロジェクトをVercelにデプロイすると、自動的にサーバーレス関数として設定されます。
  • Netlify: @astrojs/netlify アダプターを使用します。Netlify Functionsとしてデプロイされます。
  • Cloudflare Pages: @astrojs/cloudflare アダプターを使用します。Cloudflare Workersとしてデプロイされます。
  • Node.jsサーバー: @astrojs/node アダプターを使用します。ビルド出力には、ExpressのようなNode.jsフレームワークで実行できるスクリプトが含まれます。これをPM2やDockerなどのプロセス管理ツールを使ってサーバー上で実行します。
  • Deno: @astrojs/deno アダプターを使用します。Deno Deployなどにデプロイできます。

適切なアダプターを選択し、各サービスのデプロイガイドに従ってください。Astroのドキュメンテーションには、主要なホスティングプロバイダーごとのデプロイガイドが詳しく記載されています。Reactコンポーネントを含むAstroプロジェクトのデプロイ手順は、Reactを使用しない場合と基本的に同じですが、SSRの場合はSSR対応のアダプターを正しく設定することが重要です。

AstroとReactのその他の連携

AstroとReactは、基本的なコンポーネントの組み込み以外にも様々な連携方法があります。

MDXファイル内でのReactコンポーネントの使用

AstroはMDX (Markdown + JSX) をサポートしており、MDXファイルの中にJSX構文でReactコンポーネントを直接記述できます。これは、ブログ記事やドキュメンテーションのようなコンテンツ内で、部分的にインタラクティブな要素やリッチなUIコンポーネントを埋め込みたい場合に非常に便利です。

MDXを使用するには、まず @astrojs/mdx インテグレーションを追加します。

bash
npx astro add mdx

次に、ReactコンポーネントをインポートしてMDXファイル内で使用します。

“`mdx

title: My MDX Blog Post
layout: ../layouts/BlogPostLayout.astro # レイアウトも使える
import Counter from ‘../components/Counter’; // Reactコンポーネントをインポート


{frontmatter.title}

これはMDXで書かれたブログ記事です。マークダウン記法が使えます。

js
// JavaScriptコードブロックも記述可能
const message = "Hello from MDX!";
console.log(message);

インタラクティブなReactコンポーネントを埋め込むこともできます。

{/ MDX内でReactコンポーネントを使用 /}

これはMDXの機能と、AstroのIsland Architecture(ここではclient:loadディレクティブ)を組み合わせて実現しています。

MDXファイル内で使用するReactコンポーネントにも client: ディレクティブを適用できます。これにより、MDXコンテンツ内の特定のインタラクティブ要素だけをクライアント側で有効にできます。

UIフレームワークの利用

Material UI, Ant Design, Chakra UIなどの人気のあるReact UIフレームワークをAstroプロジェクトで使用することも可能です。これらのフレームワークは通常、テーマプロバイダーや状態管理など、クライアントサイドでのJavaScript実行に大きく依存します。したがって、これらのフレームワークのコンポーネントを使用する場合、それを囲む親コンポーネントや、テーマプロバイダーなどのルートとなるコンポーネントに client:only="react" ディレクティブを付ける必要がある場合が多いです。

例:Material UIを使用する場合

  1. Material UIと関連ライブラリをインストールします。
    bash
    npm install @mui/material @emotion/react @emotion/styled

    (または yarn/pnpm)
  2. Material UIのコンポーネントを使用するReactコンポーネントを作成します。

    “`tsx
    // src/components/MuiButtonExample.tsx
    import React from ‘react’;
    import Button from ‘@mui/material/Button’;
    import Box from ‘@mui/material/Box’; // 例としてBoxコンポーネントも使用

    const MuiButtonExample: React.FC = () => {
    return (
    {/ sx propなどMUIの高度な機能を使う /}


    );
    };

    export default MuiButtonExample;
    ``
    3. Astroページで使用します。通常、
    client:only` が必要になります。

    “`astro

    import Layout from ‘../layouts/Layout.astro’;
    import MuiButtonExample from ‘../components/MuiButtonExample’;



    Using Material UI in Astro

    {/* Material UIコンポーネントはclient:onlyが必要な場合が多い */}
    <MuiButtonExample client:only="react" />
    
    <p>Note: UI libraries often require client-side rendering.</p>
    



    ``
    多くのUIフレームワークは、SSRをサポートするための特別な設定やプロバイダーコンポーネントを必要とします。AstroのSSRモードでこれらのライブラリを完全に機能させたい場合は、ライブラリのドキュメントを参照し、AstroのSSRアダプターと組み合わせて適切に設定する必要があります。
    client:only` は最も簡単な方法ですが、初期ロードのパフォーマンスには影響します。

よくある質問とトラブルシューティング

AstroでReactを使う際によく遭遇する問題とその解決策をいくつか紹介します。

  1. Hydration failed エラー:

    • 原因: サーバーサイドでレンダリングされたHTMLと、クライアントサイドでハイドレーションしようとしているReactコンポーネントの出力が一致しない場合に発生します。これは、client: ディレクティブを持つコンポーネントでよく起こります。
    • 一般的な原因:
      • サーバーサイドとクライアントサイドで異なる環境変数やデータを使用している。
      • ブラウザ固有のAPI (例: window, localStorage) に、コンポーネントのレンダリング中にアクセスしている(これらのAPIはサーバーサイドでは利用できません)。
      • <ClientOnlyComponent client:only="react" /> のように、サーバーレンダリングをスキップすべきコンポーネントに誤って他の client: ディレクティブを付けている。
      • AstroのSlot内に渡された要素が、ReactコンポーネントのPropsとして扱われる際に予期しない変更を受けている。
    • 解決策:
      • client:only="react" を使用して、コンポーネント全体をクライアントサイドでのみレンダリングするようにする。これは最も簡単な解決策ですが、パフォーマンスに影響します。
      • useEffect Hook を使用して、ブラウザAPIへのアクセスやクライアントサイドでのみ実行すべきロジックを、コンポーネントがマウントされた後に実行されるように遅延させる。
      • サーバーサイドでレンダリングする際に、ブラウザAPIにアクセスする部分を条件分岐 (typeof window !== 'undefined') でスキップする。
      • AstroのSlotを使うのではなく、Reactコンポーネントのchildrenとして直接渡す。
  2. PropsがReactコンポーネントに渡されない/期待する値にならない:

    • 原因: AstroファイルでPropsを渡す構文が間違っている、またはReactコンポーネント側でPropsを正しく受け取れていない。
    • 解決策:
      • AstroファイルでPropsを渡す際は、HTML属性のように attribute={value} の形式で記述します。文字列以外の値(数値、配列、オブジェクト、関数など)を渡す際は中括弧 {} で囲む必要があります。
      • Reactコンポーネント側でPropsを分割代入や props.propName の形式で正しく受け取っているか確認します。Propsはオブジェクトとして渡されます。
      • TypeScriptを使っている場合は、Propsの型定義がAstroファイルで渡される値と一致しているか確認します。
  3. CSSが期待通りに適用されない/競合する:

    • 原因: グローバルCSS、AstroのScoped CSS、CSSモジュール、CSS-in-JSなどが混在している場合、スタイルが互いに影響し合ったり、意図しない優先順位になったりすることがあります。
    • 解決策:
      • スタイリング手法を統一するか、それぞれの特徴を理解して使い分けます。
      • Reactコンポーネント内のスタイリングにはCSSモジュールを優先的に使用し、コンポーネント外のスタイルと分離します。
      • Astroの <style> タグはデフォルトでスコープ化されることを利用します。グローバルにしたい場合は is:global を付けます。
      • 開発者ツールの要素インスペクターで、適用されているCSSのソースと優先順位を確認します。
  4. クライアントサイドJSが期待通りに実行されない:

    • 原因: Reactコンポーネントに client: ディレクティブが付いていない、または間違ったディレクティブを選択している可能性があります。
    • 解決策:
      • インタラクティブにしたいReactコンポーネントには必ず client: ディレクティブ(client:load, client:visible など)を付けます。
      • コンポーネントが期待するタイミングでハイドレーションされるように、適切な client: ディレクティブを選択します。例えば、スクロールしないと見えないコンポーネントに client:load を付けても、ユーザーがスクロールしなければ「すぐにインタラクティブになる必要がない」というAstroの最適化判断により、JavaScriptのロードが遅延される場合があります。確実にロードさせたい場合は client:load を使用します。
      • client:only を使っている場合、サーバーレンダリングは行われないため、JavaScriptのロード・実行が完了するまでコンポーネントは表示されません。

開発中は npm run dev コマンドを使用し、ブラウザの開発者ツールでネットワークタブやコンソールを確認することが、これらの問題をデバッグする上で非常に役立ちます。特に、どのJavaScriptファイルがロードされているか、いつロードされているかを確認することで、client: ディレクティブが正しく機能しているかを判断できます。

まとめ:AstroとReactの強力な組み合わせ

この記事では、AstroでReactを使用する方法について、導入から詳細な活用までを網羅的に解説しました。

Astroの最大の強みは、パフォーマンスを最優先したIsland Architectureにあります。ページ全体のJavaScriptをデフォルトで削減し、必要な部分だけをインタラクティブな「島」としてハイドレーションすることで、非常に高速なウェブサイトを実現します。

一方、Reactはコンポーネントベースの開発、宣言的なUI、広範なエコシステムによる開発効率の高さが魅力です。

AstroとReactを組み合わせることで、これらの両方のメリットを享受できます。既存のReactコンポーネント資産を活用しながら、コンテンツ表示はAstroによるSSG/SSRで超高速に行い、リッチなインタラクティブ性が必要な特定の箇所にだけReactコンポーネントを配置し、client: ディレクティブでハイドレーションのタイミングを制御できます。これにより、「ほとんど静的だが、必要な箇所はリッチでインタラクティブ」という、現代のウェブサイトに求められる多くの要件を満たすサイトを、効率的に開発することが可能です。

記事中で解説した主な内容は以下の通りです。

  • Astroの基本概念(Island Architecture, SSG/SSR)
  • Reactの基本概念(コンポーネント、仮想DOM)
  • AstroとReactを組み合わせるメリット
  • プロジェクトのセットアップとReactインテグレーションの追加
  • 静的なReactコンポーネントの表示(サーバーレンダリング)
  • client: ディレクティブを使ったインタラクティブ性の追加(クライアントハイドレーション)と各ディレクティブの詳細
  • React Hooksと状態管理(useState, useEffect, Context API, 状態管理ライブラリ)
  • AstroからReactコンポーネントへのProps渡し
  • 多様なスタイリング手法(Astroスタイル、CSSモジュール、CSS-in-JS)
  • データの取得(Astro側 vs React側)
  • TypeScriptによる型安全な開発
  • パフォーマンス最適化のヒント
  • SSR環境での利用
  • 既存Reactプロジェクトからの移行戦略
  • デプロイ方法
  • MDXやUIフレームワークとの連携
  • よくある問題とトラブルシューティング

AstroとReactの組み合わせは、特に静的なコンテンツが多く、かつ部分的にリッチなインタラクションが必要なウェブサイトやアプリケーション(ブログ、マーケティングサイト、Eコマースサイトの一部、ドキュメンテーション、ポートフォリオなど)にとって、非常に強力な選択肢となるでしょう。SSGとSSRを柔軟に使い分けられる点も大きな利点です。

このガイドが、あなたがAstroとReactを使って、高速で効率的なウェブサイトを構築するための一助となれば幸いです。ぜひ実際に手を動かして、そのポテンシャルを体験してみてください。

Happy Coding!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール