React Server Components(RSC)は、Reactアプリケーションのレンダリング方法にパラダイムシフトをもたらしました。コンポーネントはサーバー上でのみ実行され、クライアントに送信されるJavaScriptはゼロバイトです。これは従来のSSRとは異なります。SSRもサーバーでレンダリングしますが、ハイドレーションのためにすべてのJavaScriptをクライアントに送信します。RSCは特別なシリアライズ形式(RSCペイロード)を生成し、クライアント上のReactがコンポーネントコードを実行せずにツリーを再構築します。
最大のメリットはクライアントサイドJavaScriptの劇的な削減です。レイアウトコンポーネント、データフェッチロジック、インタラクションが不要な表示要素はすべてサーバー上で実行されます。バンドルが小さくなることで、ページ読み込みの高速化、メモリ使用量の削減、Core Web Vitalsの改善が実現します。
サーバーとクライアントの境界
'use client'と'use server'ディレクティブが境界を定義します。ディレクティブがないコンポーネントは、RSCをサポートするフレームワーク(Next.js App Router、Hydrogenなど)ではデフォルトでサーバーコンポーネントになります。
// デフォルトでサーバーコンポーネント
import { db } from "@/lib/db";
export default async function ProductList() {
const products = await db.query("SELECT * FROM products");
return (
<ul>
{products.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
// クライアントコンポーネント
"use client";
import { useState } from "react";
export default function LikeButton({ productId }) {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "♥" : "♡"}
</button>
);
}
重要なルール:サーバーコンポーネントはstate、effects、ブラウザAPIを使用できません。サーバーからクライアントに渡すpropsはシリアライズ可能である必要があります(関数、クラスインスタンス、循環参照は不可)。クライアントコンポーネントはコンポジションを通じてサーバーコンポーネントをインポートできますが(子要素やpropsとして渡す)、直接インポートすることはできません。
ストリーミングSSRとSuspense
RSCは標準でストリーミングを可能にします。Suspense境界ごとにデータの準備ができた時点で個別にストリーミングされ、ページ全体のレンダリング完了を待つ必要がありません。
export default function Page() {
return (
<div>
<Header />
<Suspense fallback={<ProductSkeleton />}>
<ProductList />
</Suspense>
<Suspense fallback={<ReviewSkeleton />}>
<Reviews />
</Suspense>
</div>
);
}
この例では、Headerが即座にレンダリングされ、ProductListとReviewsは各データベースクエリの完了後に順次レンダリングされます。ブラウザはコンテンツを段階的に表示するため、TTFB(初回バイト到達時間)とLCP(Largest Contentful Paint)が大幅に改善します。
| 指標 | 従来のSSR | ストリーミングRSC |
|---|---|---|
| TTFB | 全ページ完了後 | 最初のSuspense境界完了後 |
| LCP | ハイドレーション後 | 最初のストリームチャンク後 |
| JSバンドル | アプリ全体 | クライアントコンポーネントのみ |
| インタラクティブまで | 全ハイドレーション後 | ページごとに早期に |
データフェッチパターン
RSCの最も変革的な機能は、APIレイヤーを介さずにコンポーネント内で直接データベースクエリを実行できることです。データを使用する場所でクエリを記述するため、従来のクライアントサイドデータフェッチで問題となっていたウォーターフォールが解消されます。
async function ProductDetails({ id }) {
const product = await db.query(
"SELECT * FROM products WHERE id = $1",
[id]
);
const reviews = await db.query(
"SELECT * FROM reviews WHERE product_id = $1",
[id]
);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<ReviewList reviews={reviews} />
</div>
);
}
Reactはレンダリング中のfetch()呼び出しを自動的に重複排除します。React.cache()関数はこの重複排除をデータベースクエリを含む任意の非同期処理に拡張します。再検証戦略としては、時間ベース(ISR)、オンデマンド(webhook)、ミューテーション発動型の3つが利用可能です。
Server Actionsによるミューテーション
'use server'ディレクティブで定義されるServer Actionsは、APIルートを構築せずにフォーム送信とデータミューテーションを処理します。
async function createProduct(formData) {
"use server";
const name = formData.get("name");
const price = formData.get("price");
await db.query(
"INSERT INTO products (name, price) VALUES ($1, $2)",
[name, price]
);
revalidatePath("/products");
}
export default function ProductForm() {
return (
<form action={createProduct}>
<input name="name" required />
<input name="price" type="number" required />
<button type="submit">作成</button>
</form>
);
}
Server Actionsはプログレッシブエンハンスメントをサポートしており、JavaScriptがなくてもフォームは動作します。JavaScriptが利用可能な場合はfetch経由で送信され、サーバーは影響を受けるコンポーネントのみを再レンダリングします。エラーハンドリングと楽観的更新はReact 19のuseActionStateなどのクライアントフックで管理します。
既存プロジェクトへの導入戦略
Next.jsのPages RouterからApp Routerへの移行は段階的に行えます。移行期間中は両方のルーターを並行して運用可能です。
- データコンポーネントから始める: 静的なページやデータ量の多いページをRSCに移行
- クライアント境界を特定: state、effects、イベントハンドラを使用するコンポーネントを
'use client'に - インタラクティブな島を抽出: クライアントコンポーネントは小さく末端に保つ
- データフェッチを上位に移動: 親サーバーコンポーネントでデータを取得し、propsとして子に渡す
すべてのアプリケーションがRSCの恩恵を受けられるわけではありません。高度にインタラクティブなダッシュボード、リアルタイムコラボレーションツール、ほとんどのコンポーネントがstateやeffectsを使用するアプリケーションでは、従来のクライアントサイドレンダリングやSSRの方が適切な場合があります。
パフォーマンスとバンドルへの影響
RSCの最も測定可能な影響はJavaScriptバンドルの削減です。従来クライアントにJavaScriptとして送信されていたレイアウトコンポーネントやデータフェッチロジックが、サーバー上でのみ実行されるようになります。RSCペイロードはコンパクトなバイナリ形式で、クライアント上のReactが効率的に処理できます。
ネットワークタブでの分析では、RSCレスポンスは特殊なコンテンツタイプのストリームチャンクとして表示されます。各チャンクは解決されたSuspense境界に対応し、ブラウザはコンテンツを段階的に受信・表示するため、JavaScriptバンドルのサイズ測定だけでは評価できない知覚パフォーマンスの向上が得られます。
現在の状況と展望
2024年時点で、React Server Componentsは本番環境での採用が定着しました。Next.js 14はRSCを中核とするApp Routerを安定版として提供し、ReactチームはRSC互換の状態管理ソリューションやツールの改善を進めています。Hydrogen(Shopify)やRedwoodJSなどの新しいフレームワークもRSCを主要なレンダリングモデルとして採用しています。
学習曲線やサーバー/クライアント境界の設計判断に課題はありますが、コンテンツリッチなサイト、Eコマースプラットフォーム、ページ読み込みパフォーマンスが重要なアプリケーションでは、JavaScript削減とストリーミング改善のメリットが学習コストを大きく上回ります。
