react-server-components について
React Server Components とサーバー主導の描画における役割を解説する学習スキルです。クライアント側の JavaScript を減らしたい React 開発者向けに、SSR や対話型のクライアントコンポーネントとの違いも説明します。
主な機能
- 01サーバーでデータを取得して描画
- 02サーバーコンポーネントのコードをクライアントへ送らない
- 03クライアントコンポーネントで操作性を実現
- 04自動コード分割に対応
- 05サーバー側の再取得時にクライアント状態を維持
活用シーン
- 01少ないクライアント JavaScript で非対話型 UI を描画
- 02ブラウザー依存の操作をクライアントコンポーネントに分離
- 03入力状態を保ちながらサーバー側の結果を更新
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill react-server-componentsインストール後、Claude Code で/react-server-componentsを入力して読み込まれたか確認します。
詳細
React サーバーコンポーネント
目次
React のサーバーコンポーネントを使うと、サーバー主導のメンタルモデルによるモダンな UXを実現できます。これはコンポーネントのサーバーサイドレンダリング(SSR)とは大きく異なり、クライアント側の JavaScript バンドルを大幅に小さくできます。
使用する場合
- データ取得とレンダリングをサーバーで実行して、クライアント側の JavaScript を減らしたい場合に使用します
- Next.js 13+ App Router で、JavaScript のコストがゼロのサーバーレンダリングコンポーネントを使ってパフォーマンスを向上させるのに役立ちます
使用しない場合
- コンポーネントにクライアント側のインタラクティブ性が必要な場合 — 状態(
useState)、エフェクト(useEffect)、イベントハンドラーにはクライアントコンポーネントが必要です - ブラウザー専用の API(例:
window、localStorage、IntersectionObserver)に依存するコンポーネントの場合 - コンポーネントがすでに小さく、サーバーとクライアントの境界によって、得られるメリットよりも複雑さが増す場合
手順
- データ取得とインタラクティブでない UI には、サーバーコンポーネント(Next.js App Router などのフレームワークではデフォルト)を使用します
- インタラクティブ性(イベントハンドラー、状態、エフェクト)が必要なコンポーネントにのみ、
'use client'ディレクティブを追加します - サーバーコンポーネントでは、重いライブラリ(Markdown パーサー、日付フォーマッター)をクライアントバンドルのコストなしで使用できます
- サーバーコンポーネントは SSR を補完するものであり、置き換えるものではありません
- フレームワークがサポートしている場合は、フォームの送信や変更処理にサーバー関数またはサーバーアクション(
'use server')を使用します
詳細
更新(React 19/モダンなフレームワーク): React サーバーコンポーネントは、フレームワークのユーザーが本番環境で利用できる機能になりました。従来の SSR とは異なり、RSC ではコンポーネントの JavaScript をクライアントに送信せずに、UI の一部をサーバーでレンダリングできます。コンテナー/プレゼンテーションパターンが適しています。「コンテナー」をデータを取得してインタラクティブなクライアントコンポーネントに渡すサーバーコンポーネントにできます。
Next.js App Router では、
getServerSidePropsはもう使いません。代わりに、app/ディレクトリ内の任意の React コンポーネントを非同期にして、サーバーでデータを取得できます。React サーバーコンポーネントは SSR の代替ではなく、補完するものです。通常、ページの大部分には RSC を使用し(SSR の一部としてレンダリングおよびストリーミングされます)、インタラクティブ性が必要なコンポーネントには'use client'ディレクティブを追加します。
'use server'ディレクティブはサーバー関数またはサーバーアクションに使用するもので、コンポーネントをサーバーコンポーネントとして指定するためのものではありません。サーバーコンポーネントにはディレクティブがありません。サーバーコンポーネントをサポートするフレームワークでは、'use client'を指定しない限りデフォルトでサーバーコンポーネントになります。
React サーバーコンポーネントは、Next.js App Router などのフレームワークで本番利用が可能になりました。以下のリソースが、学習を始める際に特に役立ちます。
- React サーバーコンポーネントのリファレンスは、現時点で最も優れた概要です。
- RFCは、実装の背景を詳しく知るうえで今も役立ちます。
- サーバーコンポーネントのモダンなアプローチについては、Next.js App Router のドキュメントを参照してください
- Shopify Hydrogen とサーバーコンポーネント
サーバーサイドレンダリングの制限
現在のクライアントサイド JavaScript のサーバーサイドレンダリングは、最適とは限りません。コンポーネントの JavaScript はサーバー上で HTML 文字列にレンダリングされます。この HTML がブラウザーに配信されるため、First Contentful Paint や Largest Contentful Paint が速くなったように見えることがあります。
ただし、インタラクティブにするには依然として JavaScript を取得する必要があり、多くの場合、これはハイドレーションの段階を通じて実現されます。通常、サーバーサイドレンダリングは初回のページ読み込みに使用されるため、ハイドレーション後に再び使用されることはほとんどありません。
注: SSR を活用し、クライアントでのハイドレーションを一切行わないサーバー専用の React アプリを構築できるのは事実です。しかし、モデルにおける高度なインタラクティブ性には、React の外に出ることが伴う場合が多くあります。Server Components が実現するハイブリッドモデルでは、コンポーネントごとにこれを決定できます。
React Server Components を使用すると、コンポーネントを定期的に再取得できます。新しいデータが届くと再レンダリングするコンポーネントを含むアプリケーションをサーバー上で実行することで、クライアントに送信するコードの量を抑えられます。
Server Components が登場する前は、Markdown レンダラーとサニタイザーを使用すると、バンドルサイズが増加していました。
// *Before* Server Components
import marked from "marked"; // 35.9K (11.2K gzipped)
import sanitizeHtml from "sanitize-html"; // 206K (63.3K gzipped)
function NoteWithMarkdown({text}) {
const html = sanitizeHtml(marked(text));
return (/* render */);
}
Server Components
React の新しい Server Components はサーバーサイドレンダリングを補完し、JavaScript バンドルに追加することなく、中間的な抽象化形式へのレンダリングを可能にします。これにより、状態を失うことなくサーバーツリーとクライアントツリーを統合できるだけでなく、より多くのコンポーネントへの拡張も可能になります。
Server Components は SSR の代替ではありません。両者を組み合わせると、中間形式で素早くレンダリングした後、サーバーサイドレンダリングのインフラストラクチャで HTML にレンダリングできます。そのため、初期描画を引き続き高速にできます。Server Components が出力する Client コンポーネントを SSR します。これは、ほかのデータ取得方式で SSR を使用する方法と同様です。
ただし今回は、JavaScript バンドルが大幅に小さくなります。初期の調査では、バンドルサイズを大幅に削減できる可能性(-18-29%)が示されていますが、実環境での効果について React チームがより明確に把握できるのは、さらなるインフラストラクチャの作業が完了してからです。
import marked from "marked"; // zero bundle size
import sanitizeHtml from "sanitize-html"; // zero bundle size
function NoteWithMarkdown({text}) {
// same as before
}
自動コード分割
コード分割を使用して、ユーザーが必要とするコードだけを必要なタイミングで配信することは、ベストプラクティスと見なされてきました。これにより、アプリをより小さなバンドルに分割し、クライアントに送信するコードの量を減らせます。Server Components が登場する前は、React.lazy() を手動で使用して「分割ポイント」を定義するか、ルートやページなどのメタフレームワークが設定するヒューリスティックに依存して、新しいチャンクを作成していました。
Server Components が登場する前:
// *Before* Server Components
import React from "react";
// one of these will start loading *when rendered on the client*:
const OldPhotoRenderer = React.lazy(() => import("./OldPhotoRenderer.js"));
const NewPhotoRenderer = React.lazy(() => import("./NewPhotoRenderer.js"));
function Photo(props) {
// Switch on feature flags, logged in/out, type of content, etc:
if (FeatureFlags.useNewPhotoRenderer) {
return <NewPhotoRenderer {...props} />;
} else {
return <PhotoRenderer {...props} />;
}
}
コード分割には、次のような課題があります。
- Next.js のようなメタフレームワークを使用しない場合、多くの場合、この最適化に手動で取り組み、
import文を動的インポートに置き換える必要があります。 - コンポーネントの読み込みが始まるタイミングが遅れ、ユーザーエクスペリエンスに影響することがあります。
Server Components では、Client コンポーネント内のすべての通常のインポートをコード分割ポイントの候補として扱うことで、自動コード分割を実現します。また、開発者はどのコンポーネントを使用するかを(サーバー上で)より早い段階で選択できるため、クライアントはレンダリングプロセスのより早い段階でそのコンポーネントを取得できます。
Server Components を使用する場合:
import React from "react";
// one of these will start loading *once rendered and streamed to the client*:
import OldPhotoRenderer from "./OldPhotoRenderer.client.js";
import NewPhotoRenderer from "./NewPhotoRenderer.client.js";
function Photo(props) {
// Switch on feature flags, logged in/out, type of content, etc:
if (FeatureFlags.useNewPhotoRenderer) {
return <NewPhotoRenderer {...props} />;
} else {
return <PhotoRenderer {...props} />;
}
}
Server Components は Next.js SSR に取って代わるのでしょうか?
いいえ。両者はまったく異なるものです。Server Components の初期導入は、研究や実験が続くなかで、Next.js のようなメタフレームワークを通じて試されることになります。
Dan Abramov による Next.js SSR と Server Components の違いをまとめると、次のとおりです。
- Server Components のコードがクライアントに配信されることはありません。 React を使った SSR の多くの実装では、コンポーネントのコードも JavaScript バンドルを通じてクライアントに送信されます。そのため、インタラクティブになるまでに時間がかかる場合があります。
- Server Components を使うと、ツリー内のどこからでもバックエンドにアクセスできます。 以前の Next.js のデータ API では、ロジックは多くの場合、
getServerSideProps()のようなページレベルの関数に置く必要がありました。Server Components では、データの取得処理を、それを必要とするコンポーネントの近くに置くことができます。 - Server Components は、ツリー内のクライアント側の状態を維持したまま再取得できます。 これは、主要な転送方式が HTML だけよりもはるかに豊富なためです。サーバーでレンダリングされた部分(検索結果の一覧など)を再取得しても、その内部の状態(検索入力テキスト、フォーカス、テキスト選択など)を失わずに済みます。
Server Components の初期の統合作業の一部は、次の処理を行う webpack プラグインを通じて進められます。
- すべてのクライアントコンポーネントを特定します
- ID からチャンク URL への対応表を作成します
- Node.js ローダーが、クライアントコンポーネントへのインポートをこの対応表への参照に置き換えます。
- この作業の一部には、より深い統合(ルーティングなどの要素との統合)が必要になります。そのため、Next.js のようなフレームワークで動作させることに価値があります。
Dan が述べているように、この取り組みの目標の一つは、メタフレームワークを大幅に改善できるようにすることです。