Create React Appからの移行
AstroのReactインテグレーションは、Astroコンポーネント内でReactコンポーネントを活用する機能を提供します(詳しくはAstroでフレームワークコンポーネントを使う)。これにより、Create React App (CRA) のReactアプリ全体をAstroコンポーネント内で再利用することも可能です。
---
// CRAプロジェクトのルートAppコンポーネントをインポート
import App from '../cra-project/App.jsx';
---
// クライアントディレクティブでアプリを読み込む
<App client:load />多くのアプリは、Reactインテグレーションを追加するだけでそのまま動作します。まずはこの方法でプロジェクトをすぐに起動し、機能を保ったまま段階的にAstroへ移行していくのがおすすめです。
時間をかけて、構造を.astroと.jsxの組み合わせに変更していきましょう。実際には想像より少ない数のReactコンポーネントで済むケースが多いはずです。
ここでは、移行開始に役立つ主要な概念と戦略を紹介します。さらに詳しくはドキュメント全体やDiscordコミュニティをご活用ください。
CRAとAstroの類似点
.astroファイルの構文はJSXとよく似ています。Astroを使うのも直感的に感じられるはずです。- Astroはファイルベースのルーティングを採用し、動的ルート (EN)もファイル名で定義できます。
- Astroはコンポーネントベースです。マークアップ構造自体は移行前後で大きく変わりません。
- React・Preact・Solid用の公式インテグレーションがあり、既存のJSXコンポーネントをそのまま利用できます。ただし、これらのファイルはAstro内では
.jsxまたは.tsx拡張子を持つ必要があります。 - AstroはNPMパッケージのインストール (EN)をサポートしており、Reactライブラリも含まれます。多くの既存依存関係はAstroでも動作することが多いでしょう。
CRAとAstroの主な相違点
CRAサイトをAstroで再構築すると、いくつか重要な違いに気づくでしょう。
- CRAは単一ページアプリケーションで、
index.jsをプロジェクトのルートとして使用します。Astroプロジェクトはマルチページサイトで、index.astroがホームページとなります。 .astroコンポーネントは、“ページテンプレートを返すようなexport関数”ではなく、コードフェンス(---)でJavaScriptコードを区切り、その下に生成するHTMLを直接記述する構造を取ります。- Astroはコンテンツ駆動設計です。CRAが高いクライアント側インタラクティブ性を前提とする場合、ダッシュボードなどはAstroアイランドやクライアントディレクティブを活用して再現する必要があります。
CRAをAstroに組み込む
既存のCRAアプリをAstroでそのまま描画し、段階的に変換していく方法を紹介します。
新しいAstroプロジェクトを作成する
パッケージマネージャでcreate astroコマンドを実行し、"empty"テンプレートを選択して新しいプロジェクトを作成します。
npm create astro@latest
pnpm create astro@latest
yarn create astro@latest
インテグレーションと依存関係を追加する
astro addコマンドでReactインテグレーションを導入します。TailwindやMDXが必要なら同時に指定できます。
npx astro add react
npx astro add react tailwind mdx
pnpm astro add react
pnpm astro add react tailwind mdx
yarn astro add react
yarn astro add react tailwind mdx
CRAが追加のNPMパッケージを必要としている場合は、それらを新しいAstroプロジェクトのpackage.jsonに追記し、インストールしてください。多くのReact依存はAstroでも互換性がありますが、すべてが動作するとは限りません。
既存アプリのファイルを追加する
既存CRAプロジェクトのソースフォルダ(components、hooks、stylesなど)を構造を保ったままsrc/cra-project/にコピーし、.jsは.jsxまたは.tsxへリネームします。設定ファイルはコピーせず、Astro側のastro.config.mjsやtsconfig.jsonを使用します。public/配下の静的アセットはAstroのpublic/へ移動します。
ディレクトリpublic/
- logo.png
- favicon.ico
- ...
ディレクトリsrc/
ディレクトリcra-project/
- App.jsx
- ...
ディレクトリpages/
- index.astro
- astro.config.mjs
- package.json
- tsconfig.json
アプリをレンダリングする
src/pages/index.astroのフロントマターでCRAのルートコンポーネントをインポートし、ページテンプレート部分で<App />を描画します。
---
import App from '../cra-project/App.jsx';
---
<App client:load />:::note[クライアントディレクティブ]アプリをインタラクティブに動かすには、クライアントディレクティブ の指定が必要です。AstroはReactアプリをまず静的HTMLとしてビルドし、client:~が付いたコンポーネントだけをクライアント側で実行します。
client:load:サーバー側でレンダリングした後、ページの読み込み時にすぐクライアントで起動します。client:only="react":サーバー側レンダリングをせず、完全にクライアントサイドでのみ実行します。:::
CRAをAstroに変換する
既存アプリをAstroに組み込んだ後、次はアプリ自体をAstroへ変換していきます。
基本構造にはAstro HTMLテンプレートコンポーネントを使い、インタラクティブな部分には個別のReactコンポーネント(場合によってはそれ自体が小さなアプリになっていることもあります)をインポートして配置します。
移行のアプローチはプロジェクトごとに異なりますが、動作中のアプリを中断せず段階的に進められます。無理のないペースで個別の機能を変換し、時間をかけて徐々にAstroコンポーネントで動かす範囲を広げましょう。
Reactアプリを変換する際には、どのReactコンポーネントをAstroコンポーネントとして書き直すかを決めます。制約は次のとおりです。AstroコンポーネントはReactコンポーネントをインポートできますが、Reactコンポーネントは他のReactコンポーネントのみをインポートしなければなりません。
---
import MyReactComponent from '../components/MyReactComponent.jsx';
---
<html>
<body>
<h1>Astroから直接Reactコンポーネントを使おう!</h1>
<MyReactComponent />
</body>
</html>AstroコンポーネントをReactコンポーネント内でインポートするのではなく、1つのAstroコンポーネントの中にReactコンポーネントをネストすることもできます。
---
import MyReactSidebar from '../components/MyReactSidebar.jsx';
import MyReactButton from '../components/MyReactButton.jsx';
---
<MyReactSidebar>
<p>ここはテキストとボタンを含むサイドバーです。</p>
<div slot="actions">
<MyReactButton client:idle />
</div>
</MyReactSidebar>CRAをAstroプロジェクトとして再構築する前に、Astroアイランド と Astroコンポーネント について学んでおくと、移行がスムーズになります。
JSX vs Astro
以下に、CRAコンポーネントと対応するAstroコンポーネントの例を示します。
import React, { useState, useEffect } from 'react';
import Header from './Header';
import Footer from './Footer';
const Component = () => {
const [stars, setStars] = useState(0);
const [message, setMessage] = useState('');
useEffect(() => {
const fetchData = async () => {
const res = await fetch('https://api.github.com/repos/withastro/astro');
const json = await res.json();
setStars(json.stargazers_count || 0);
setMessage(json.message);
};
fetchData();
}, []);
return (
<>
<Header />
<p style={{
backgroundColor: `#f4f4f4`,
padding: `1em 1.5em`,
textAlign: `center`,
marginBottom: `1em`
}}>Astro has {stars} 🧑🚀</p>
<Footer />
</>
);
};
export default Component;
---
import Header from './Header.astro';
import Footer from './Footer.astro';
import './layout.css';
const res = await fetch('https://api.github.com/repos/withastro/astro')
const json = await res.json();
const message = json.message;
const stars = json.stargazers_count || 0;
---
<Header />
<p class="banner">Astro has {stars} 🧑🚀</p>
<Footer />
<style>
.banner {
background-color: #f4f4f4;
padding: 1em 1.5em;
text-align: center;
margin-bottom: 1em;
}
</style>
JSXファイルから.astroファイルへの変換
以下は、CRAの.jsコンポーネントを.astroコンポーネントへ変換する際のポイントです。
- 既存のCRAコンポーネント関数が返すJSXを、HTMLテンプレートのベースとして利用します。
{children}やclassNameなど、Astro構文リファレンスをAstro構文またはHTML標準属性へ置き換えます。- import文を含む必要なJavaScriptはすべて
コードフェンス(---)内へ移動します。※条件付きレンダリングに使うJavaScriptはテンプレート内に直接記述できます。 - 追加プロパティはAstro.props (EN)で取得します。
- インポートしているコンポーネントを
.astroへ変換するか検討します。現在のままReactコンポーネントとして残すこともできますが、インタラクティブ性が不要な場合は将来的に.astroへ書き換えると軽量化できます。 useEffect()で行っていたデータ取得は、import文やimport.meta.glob()でローカルファイルを読み込むか、fetch()で外部データを取得する方法へ置き換えます。
テストの移行
Astroは静的HTMLを出力するため、ビルド後のファイルを使ったE2Eテスト(エンドツーエンドテスト)が可能です。既存のE2Eテスト(JestやPlaywrightなど)が、CRAサイトのマークアップを忠実に再現できていれば、そのまま動くケースも多いでしょう。また、React Testing LibraryやJestをインポートしてAstro上でReactコンポーネントのテストを行うことも可能です。詳細は テストガイド を参照してください。
参照: CRAをAstroに変換
CRAのインポートをAstroに変換
ファイルインポート (EN)を必ず正しい相対パスで記述します。インポートエイリアス (EN)を使うか、フルパスを書くかのいずれかです。
.astroを含む一部のファイル型は拡張子を省略できません。拡張子まで明示してインポートしてください。
---
import Card from '../../components/Card.astro';
---
<Card />CRAのchildrenプロップスをAstroへ
{children}をAstroの<slot />に置き換えます。Astroでは{children}を関数プロップとして受け取る必要はなく、子要素は自動的に<slot />へ描画されます。
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>複数の子要素を渡すReactコンポーネントは、名前付きスロットを使ってAstroへ移行できます。詳しくはスロットの使い方を参照してください。
CRAのデータフェッチ方法をAstroに置き換える
Create React Appでのデータ取得はAstroでも似ていますが、いくつか違いがあります。サイドエフェクトフック(useEffect())によるローカルファイル取得は削除し、代わりにimport.meta.glob()またはgetCollection()/getEntry()を使います。リモートデータはfetch()を利用します。これらの処理はフロントマター(---内)でtop-level awaitを使って実行します。
---
import { getCollection } from 'astro:content';
// src/content/blog/以下のエントリを取得
const allBlogPosts = await getCollection('blog');
// src/pages/post/以下の.mdファイルを取得
const allPosts = Object.values(import.meta.glob('../pages/post/*.md', { eager: true }));
// リモートデータを取得
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---import.meta.glob()によるローカルファイル取得- Collections APIのクエリ: /ja/guides/content-collections/#コレクションのクエリ (EN)
- リモートデータのフェッチ: /ja/guides/data-fetching/ (EN)
CRAのスタイリングをAstroへ
AstroではCSS-in-JSライブラリ(例:styled-components)がそのまま使えない場合があります。必要に応じて他のCSS手法へ置き換えてください。
- インラインスタイルオブジェクト(
style={{ fontWeight: "bold" }})はHTMLのインライン属性(style="font-weight: bold;")へ変換します。 - または、Astroの
<style>タグ (EN)を使ってスコープ付きCSSを記述します。
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>Tailwindを使う場合はTailwind Viteプラグイン (EN)をインストールするだけで、既存のTailwindコードを変更せずに利用できます。詳しくはAstroでのスタイリング (EN)を参照してください。
トラブルシューティング
多くのCRAはAstroでそのまま動作しますが、機能やスタイルを完全に再現するには細かい調整が必要になることがあります。もし解決策が見つからない場合は、Astro Discord で質問してください。
コミュニティリソース
:::note[共有したいリソースはありますか?]もし、Create React AppをAstroに移行するのに役立つビデオやブログ記事を見つけた見つけた、あるいは自分で作成した役立つ動画やブログ記事があれば、このリストに追加してください。:::