React Server Components(RSC)とは?サーバー・クライアントの違いと使い分けを実装例で解説
React Server Components(RSC)は、コンポーネントをサーバー上でのみ実行し、その結果だけをクライアントへ送るReactの仕組みです。ブラウザに送るJavaScriptを減らしながら、データ取得をサーバー側で完結できます。React 19で安定版となりマイナーバージョン間の破壊的変更がなくなり、Next.jsのApp Routerではすべてのコンポーネントがデフォルトでサーバーコンポーネントとして扱われます。混乱しやすいのは「サーバーコンポーネントを表す専用ディレクティブは存在しない」点で、目印を付けるのは逆に'use client'を書くクライアントコンポーネントの側です。本記事では、サーバーとクライアントの使い分け、SSRとの違い、Next.jsでの実装、そして使うべきでない場面までを実コード付きで整理します。
まとめ:RSC導入前に押さえる4点
- デフォルトはサーバー:App Routerではコンポーネントは既定でサーバー実行。
useState・onClick・ブラウザAPIを使う部分だけ'use client'でクライアント化する。 - 専用ディレクティブは無い:サーバーコンポーネントに目印は不要。
'use server'はサーバーコンポーネントの印ではなく、クライアントから呼べるサーバー関数(Server Actions)に付けるもの。 - SSRとは別の概念:SSRは「HTMLをサーバーで生成する描画方式」、RSCは「どのコンポーネントをサーバーで実行しJSを送らないかの分担」。両立して使う。
- 旧命名は廃止:
.server.jsx/.client.jsxという拡張子での区別は2020年の実験的仕様で、現在は使わない。ディレクティブで区別する。
React Server Components(RSC)とは
RSCは、レンダリングをサーバー側で行い、その結果(描画済みの内容)をクライアントへ渡すコンポーネントです。従来のReactコンポーネントがブラウザで実行されバンドルに含まれていたのに対し、サーバーコンポーネントのコードはクライアントのJavaScriptバンドルに含まれません。そのため、Markdownパーサーや日付整形ライブラリのような重い依存を使っても、ブラウザに送るコード量は増えません。
サーバーコンポーネントはサーバー上でデータベースやファイルシステムへ直接アクセスでき、async/awaitでデータ取得をコンポーネント内に書けます。一方で、useState・useEffectなどのフックやクリックなどのイベントハンドラは持てません。ユーザー操作に反応する部分は、後述するクライアントコンポーネントが担当します。
React 19でRSCの仕様は安定版となり、マイナーバージョン間で壊れないことが保証されました。実際に本番で使う際は、RSCを実装したフレームワーク(後述のNext.js App Routerが最も成熟)を通じて利用するのが前提です。React単体に「RSCを有効化するAPI」を書くわけではありません。
サーバーコンポーネントとクライアントコンポーネントの違いと使い分け
検索でも「サーバー vs クライアント」の使い分けが最も多く問われます。判断の起点はシンプルで、その部分にインタラクティブ性(状態・イベント・ブラウザAPI)が要るかだけです。要らなければサーバー、要ればクライアントに置きます。
| 観点 | サーバーコンポーネント(既定) | クライアントコンポーネント('use client') |
|---|---|---|
| 実行場所 | サーバーのみ | サーバーで初期HTML生成後、ブラウザでも実行 |
| useState / useEffect | 使えない | 使える |
| onClick等のイベント | 付けられない | 付けられる |
| DB・ファイルへの直接アクセス | できる | できない(API経由) |
| JSバンドルへの含有 | 含まれない | 含まれる |
| 向く用途 | データ取得・静的表示・重い依存の利用 | フォーム・モーダル・タブなどの操作UI |
‘use client’境界の最小化
クライアント化はファイル単位で、先頭に'use client'を書きます。重要なのは、ツリー全体ではなく「操作が必要な葉」だけをクライアントにすることです。'use client'を付けたコンポーネントからインポートされる子は自動的にクライアント側に入るため、境界を根に近づけるほどバンドルは膨らみます。
// LikeButton.tsx — 操作するのでクライアント
'use client';
import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>{liked ? '♥' : '♡'}</button>;
}
// Article.tsx — データ取得は上位のサーバーコンポーネントで
import { LikeButton } from './LikeButton';
export default async function Article({ id }) {
const post = await db.post.find(id); // サーバーで直接取得
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
<LikeButton />
</article>
);
}
サーバーコンポーネントは、クライアントコンポーネントにchildrenやprops経由でシリアライズ可能な値を渡せます。関数やクラスインスタンスなどシリアライズできない値は渡せない点に注意します。両方の環境で使える純粋な表示コンポーネント(いわゆる共有コンポーネント)は、'use client'を付けなければサーバー側では通常のコンポーネントとして、クライアントツリーに取り込まれればクライアント側として動きます。
どちらのコンポーネントか確認する方法
「このコンポーネントはサーバーとクライアントのどちらで動いているか」を確認したいときは、次の3つで判別できます。第一に、ファイル先頭に'use client'があればクライアント、無く親からもクライアント境界に入っていなければサーバーです。第二に、console.logを仕込むと、サーバーコンポーネントの出力はブラウザではなくターミナル(サーバー側のコンソール)に出ます。第三に、ブラウザの開発者ツールでReact Developer Toolsを見ると、サーバーコンポーネントは歯車ではなくタグ表示になり、クライアントコンポーネントと区別されます。useStateを書いてエラーになるかどうかも、サーバーコンポーネントかを見分ける実用的な手掛かりです。
RSCとSSR(サーバーサイドレンダリング)の違い
「reactサーバーサイド」で検索して両者を同じものと捉えると設計を誤ります。SSRは描画のタイミングの話、RSCはコンポーネントの実行場所の話で、レイヤーが異なります。
| SSR | RSC | |
|---|---|---|
| 何を決めるか | HTMLをいつ・どこで生成するか | どのコンポーネントをサーバーで実行するか |
| クライアントへ送るJS | 全コンポーネント分を送りハイドレート | サーバーコンポーネント分は送らない |
| 関係 | 排他ではない。RSCの初期HTMLはSSRで生成され、両者は併用される | |
従来のSSRでは、サーバーで一度HTMLを作った後、同じコンポーネントのJavaScriptをすべてクライアントへ送り、ブラウザ側で再度実行(ハイドレーション)していました。RSCはこのうち操作の要らない部分のJSを最初から送らないため、ハイドレーション対象とバンドルサイズを減らせます。SSR・CSR・SSGといった描画方式そのものの違いはSSR・CSR・SSG・ISG・ISRの違いと使い分けで、Next.jsが持つ描画方式の全体像はNext.jsの主要なレンダリング方式で整理しています。認証の観点でのサーバー/クライアントの違いはCSRとSSRの認証の違いが参考になります。
Next.js App Routerでの実装
RSCを本番で使う現実的な選択肢はNext.jsのApp Router(app/ディレクトリ)です。App Routerではファイルを作った時点でサーバーコンポーネントになり、特別な設定は要りません。フレームワーク側の最新機能はNext.js 16の概要も合わせて確認してください。
サーバーコンポーネントでのデータ取得
データ取得はコンポーネントをasyncにして直接awaitします。useEffectとローディング状態の管理は不要になり、取得したデータはサーバーで描画されるためクライアントに生データを渡す必要もありません。
// app/users/page.tsx(サーバーコンポーネント)
export default async function UsersPage() {
const res = await fetch('https://api.example.com/users', {
next: { revalidate: 60 }, // 60秒キャッシュ
});
const users = await res.json();
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}
Server Actions(’use server’)でのデータ更新
フォーム送信などの更新処理は、'use server'を付けた関数(Server Action)で書きます。これはクライアントから呼び出せるサーバー側の関数で、APIエンドポイントを別途用意せずにサーバー処理を実行できます。'use server'はサーバーコンポーネントの目印ではなく、あくまでこの「クライアントから呼べるサーバー関数」に付ける点を混同しないようにします。
// app/actions.ts
'use server';
export async function createTodo(formData: FormData) {
const title = formData.get('title');
await db.todo.create({ title });
}
// フォーム側(サーバーコンポーネントのまま送信できる)
import { createTodo } from './actions';
export default function TodoForm() {
return (
<form action={createTodo}>
<input name="title" />
<button>追加</button>
</form>
);
}
RSCを使うべきでない・慎重に検討すべき場面
RSCは万能ではなく、向かない構成もはっきりしています。次に当てはまるなら、無理に全面採用しない判断が妥当です。
- 操作が中心のUIばかりのアプリ:管理画面のダッシュボードのように、ほぼ全要素がインタラクティブなら大半が
'use client'になり、RSCの恩恵(送るJSを減らす)が薄い。従来のCSR/SPA構成のままの方が単純。 - App Routerへ移行できない既存資産:Next.jsのPages Routerや、RSC非対応の構成では利用できない。移行コストに見合うかを先に評価する。
- サーバーとクライアントの境界設計に慣れていないチーム:「サーバーコンポーネントで
useStateを書いてしまう」「クライアントコンポーネントにDBアクセスを書いてしまう」といった境界ミスが多発する。propsのシリアライズ制約やキャッシュ挙動を含め学習コストがある。 - 細かなキャッシュ制御が要件:サーバー側のデータキャッシュ・再検証(
revalidateやタグ)は強力な反面、意図しないキャッシュで「更新が反映されない」トラブルが起きやすい。キャッシュ境界を明示的に設計する必要がある。
逆に、記事・ECの一覧やLPのようにデータ取得が主で操作要素が限定的なページは、RSCがバンドル削減と初期表示の両面で効きやすい領域です。
よくある質問(FAQ)
RSCとは何ですか?
コンポーネントをサーバー上でのみ実行し、その結果だけをクライアントへ送るReactの仕組みです。そのコンポーネントのJavaScriptはブラウザに送られず、バンドルサイズを抑えられます。React 19で正式版となりました。
SSRとの違いは何ですか?
SSRは「HTMLをサーバーで生成する描画方式」、RSCは「どのコンポーネントをサーバーで実行しJSを送らないか」の分担です。レイヤーが異なり、RSCの初期HTMLはSSRで生成されるため両者は併用されます。
‘use client’はどこに書きますか?
クライアント側の動作(useStateやイベントハンドラ、ブラウザAPI)が必要なファイルの先頭に書きます。付けたファイルからインポートされる子もクライアント側に含まれるため、操作が必要な末端に近い場所へ置くのが基本です。
サーバーコンポーネントでuseStateは使えますか?
使えません。状態やイベントを扱う部分は'use client'を付けたクライアントコンポーネントに切り出します。サーバーコンポーネントはデータ取得と表示に専念させます。
.server.jsxや.client.jsxという拡張子は今も使いますか?
使いません。拡張子で区別する方式は2020年の実験的仕様で、現在は廃止されています。区別はファイル先頭の'use client'ディレクティブで行います。あわせて、Storybookについても解説しています。