先に要点
- React Server Components(RSC) は、サーバで実行され、結果(HTML / シリアライズ済みツリー)だけがクライアントに届く 新種のコンポーネント。「使った分のクライアント JS が増えない」 が最大の価値。
- サーバ専用なので、DB / 内部 API / ファイルシステム / 秘密情報を直接読める 一方、「 useState」 「useEffect」 「onClick」 などの 「状態とブラウザイベント」 系は使えない。
- インタラクション要素は 「 Client Components」(「use client」 ディレクティブを書いたファイル) に切り出す。RSC は 「データを取って木を組み立てる」、Client Components は 「動かす」 という役割分担。
- 事実上の本命採用先は Next.js App Router。Vercel が AI 時代に存在感を増す流れと同じく、バンドル削減 + ストリーミング + サーバ集約 という方向に Web 全体が動いている、その代表格。
Next.js の App Router に切り替えたら 「use client」 って書かないとエラーが出るようになった」 RSC ってよく聞くけど Server Side Rendering とどう違うの? データ取得は 「useEffect + fetch」 でいいのか、それとも 「async コンポーネント」 で書くのか分からない」 ── React と Next.js が一気に進化したことで、「書き方の前提」 が大きく変わったのが2024年以降の状況です。
ざっくり言うと、Server Components は サーバで動かす React コンポーネント で、「これまでの React コンポーネント」 とは 動く場所と使える機能 が違います。 「サーバで動くから速い」 という単純な話ではなく、「 クライアントに JS を送らない」 という選択肢を React 自身に持たせた、というのが本質的な変化 です。
この記事では、2026年5月時点の React 19 / Next.js 15 系をベースに、RSC の仕組み・Client Components との境界・データ取得のコード・落とし穴・採用判断 を、「なんとなく書いていたが理屈が掴めていない」 レベルから一段上に行けるよう整理します。
まず2行で言うと
長文を読む前に、結論だけ先に固定しておきます。
RSC は 「サーバで動く React コンポーネント」
サーバで JSX を実行 → 結果をクライアントに送る。クライアント側 JS バンドルには出ない。DB や秘密情報を直接読める 代わりに、状態 / イベント / hooks は使えない。
Client Components は 「ブラウザで動く React コンポーネント」
「use client」 を書いたファイル。「 useState」 「onClick」 「useEffect」 等の動的要素が使える。バンドルにも乗る。
RSC が Client を子として取り込める
RSC の中に Client Components を埋め込めるが、「 Client Components の子は基本 Client 側」。境界が 「ツリーの上から下」 に伝播する。
「RSC = SSR の新版」 ではなく、`SSR の世界に 「クライアント JS を載せないコンポーネント」 という新しいピースを追加したもの」 と捉えるのが正確です。
なぜ RSC が必要になったか
「React と Next.js は何が足りなくて RSC を導入したのか」 を見ると、設計意図が分かります。
② データ取得の二段構え
「 getServerSideProps」 → コンポーネントに props で渡す、という別レイヤーが必要だった。「コンポーネントから直接 DB を呼びたい」 が叶わなかった。
④ ストリーミングを活かしたい
サーバから 「準備できた部分から順番に流す」 ことで体感速度を上げたい。RSC は ツリー単位でのストリーミング と相性が良い。
つまり 「 クライアントに送らないでいい部分はサーバに留めたい」 という最適化を、コンポーネントモデルそのものに組み込んだ のが RSC です。
サーバとクライアント、それぞれで何ができて何ができないか
具体的な利用可否を表で押さえます。
| 機能 | Server Components | Client Components |
|---|---|---|
| JSX を書く | ○ | ○ |
| async / await を直接書く | ○(「export default async function ...」) | ×(基本) |
| DB クライアントを呼ぶ | ○(Prisma / Drizzle / SQL 直接) | × |
| 環境変数(秘密含む)を読む | ○(サーバから) | ×(「NEXT_PUBLIC_」 接頭辞のみ) |
| useState / useEffect / hooks | × | ○ |
| onClick / onChange など | × | ○ |
| Context を使う | △(取得は不可、子として埋めるのは可) | ○ |
| クライアント JS への影響 | なし(送らない) | あり(バンドルに乗る) |
「どの機能はどっち側か」 を意識しないと、「useState が使えない」 「event handler は使えない」 系のエラーで詰まります。 RSC 時代の React は、「 この境界を意識すること」 が新しい必須スキル です。
「use client」 ディレクティブとは
Next.js / React は、ファイルの先頭に 「'use client';」 と書くことで、「このファイル(とその関数 / コンポーネント)はクライアント側」 と明示します。
'use client';
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
このファイルからエクスポートされるものは、それを使う側に Client 境界を引きます。 RSC からは普通に 「import」 して埋め込めますが、「 use client」 を書いたファイルとその先」 はバンドルに乗る、と理解するのがコツです。
境界の伝播
「use client」 を書いたファイルから 「import」 した別ファイルは、自動的に Client 側として扱われる。「境界は上から下に流れる」 のが原則。
RSC を Client から呼ぶには
Client Components の 子として RSC を埋め込むには、「props.children」 として渡す形にする。「Client が直接 RSC を import」 はできない。
なるべく境界は下に
「 上のほうから use client」 にすると、丸ごとクライアントに送る羽目になる。「小さな葉の部分だけ Client」 が理想形。
秘密情報の漏洩防止
Client Components で 「process.env.SECRET」 のような形で書くと、ビルド時に警告 / エラーになる。「サーバでしか読まない」 ことが構造的に守られる。
「use client」 は 「 ここから先はクライアントですよ」 の境界線 であって、「このファイルをクライアントに送る」 だけのスイッチではない、というのが正確な理解です。
Next.js App Router での書き方
実際の Next.js App Router で 「データを取って表示する RSC」 はどう書くのか、コード例で確認します。
// app/users/[id]/page.tsx
import { db } from '@/lib/db';
import { LikeButton } from './LikeButton';
export default async function UserPage({ params }: { params: { id: string } }) {
const user = await db.user.findUnique({ where: { id: params.id } });
if (!user) return <div>Not found</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
<LikeButton userId={user.id} initialLikes={user.likes} />
</div>
);
}
// app/users/[id]/LikeButton.tsx
'use client';
import { useState } from 'react';
export function LikeButton({ userId, initialLikes }: { userId: string; initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes);
return (
<button onClick={() => setLikes(l => l + 1)}>♥ {likes}</button>
);
}
境界の引き方
「LikeButton」 だけ Client にして、それ以外の本体は RSC のまま。バンドルされるのはボタンだけ という設計が自然に書ける。
セキュリティの安心感
「 db.user.findUnique」 の呼び出しがクライアントに漏れる心配がない。「サーバの中だけで完結する」 と確信できる。
テストのしやすさ
RSC は基本 「関数として呼ぶ」 単位なので、「単体テストでは引数を渡して結果の JSX を検証」 という形でテストしやすい。
「データ取得用のhooks がいらなくなった」 のが、App Router の体験を大きく変えた点です。 tRPC と組み合わせる場合も、「procedure をサーバコンポーネントから直接呼ぶ」 のような構成が普通に書けます。
データ取得とキャッシュ
App Router の RSC は、「fetch を直接書く」 だけで Next.js が裏でキャッシュを管理してくれます。
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 }, // 60秒キャッシュ
});
デフォルト挙動
Next.js 15 系では、「fetch」 は 「force-cache」 ではなく 「no-store」 寄りがデフォルトに変更された。「常に最新を取る」 を基本にし、必要な場面で明示的にキャッシュする方針。
時間ベース無効化
「{ next: { revalidate: 60 } }」 のように指定すると、「60秒間はキャッシュを返す」 = ISR(Incremental Static Regeneration)的な動作になる。
タグベース無効化
「{ next: { tags: ['posts'] } }」 と書き、「revalidateTag('posts')」 でまとめて無効化。「投稿があったときだけキャッシュをクリアしたい」 ユースケースに合う。
このキャッシュ設計と Vercel の請求が高くなる原因 は密接に関係します。 「revalidate を小さくしすぎる」 と関数実行や帯域がそのまま課金に響くので、「本当に短くする必要があるか」 を毎回考えるのが大事です。
RSC のよくある詰まりどころ
実際に開発していてよく出会う罠を整理しておきます。
①「useState を Server Components で使えない」エラー
そのファイルの先頭に 「'use client';」 を書くか、「useState を使う部分だけ Client コンポーネントに切り出す」 のが正解。「コンポーネントごと Client」 にすると、無駄にバンドルが膨らむ。
② Client Component の子に async コンポーネントを書きたい
Client → Server の direct な import はできない。「props.children として上から渡す」 形に書き換える。「composition で境界を越える」 が React チームの推奨方針。
③ Context をどこに置くか
Context Provider は Client Component。RSC 内で 「useContext」 はできない。「Provider を Client、子は RSC」 という配置が普通に書ける。
④ クライアント側でだけ動くライブラリ
「 window」 を直接触るライブラリは、当然 Client Component でだけ動く。「use client」 を書いたファイルからのみ import するのが安全。
⑤ async コンポーネントのテストの書き方
RSC は 「関数自身が Promise を返す」 ので、テストでは 「await Component({...})」 のように呼んで結果の JSX を検証する。従来の 「render()」 ベースのテストとは作法が違う。
⑥ 古いライブラリの互換性
「use client」 のないままサーバで実行できない依存(「window.matchMedia」 等を import 時に触る古い UI ライブラリ)はそのままだと動かない。「use client」 でラップするか、「next/dynamic」 で SSR off にする回避策が必要。
「境界をどこに引くか」 を意識するだけで、ほとんどの問題は解決します。 慣れるまではエラーが多めですが、慣れてくると 「 自然と Client は葉先だけ」 という配置に向かう ようになります。
採用判断の軸
「RSC を採用すべきか」 の判断材料を整理しておきます。
向いている案件
① 中〜大規模 Next.js プロジェクト、② コンテンツ + 一部インタラクション、③ クライアント JS の重さがすでに問題、④ サーバとフロントを TS で1人/小チームが書く構成。
急がなくていい案件
① まだ動いている Pages Router の小規模アプリ、② SSG が中心で十分早いブログ、③ RSC 非対応のライブラリに強く依存する案件。
他フレームワークでの状況
Remix(現 React Router)、Waku、TanStack Start などでも RSC サポートが進行中。「Next.js 専用」 ではないことを覚えておくと、将来の選択肢が広がる。
学習時間の目安
すでに React に慣れている人なら、「境界の感覚」 を掴むのに 2 日〜1 週間程度。「use client」 を脳内で 「読む」 練習を意識的にすると速い。
`新規プロジェクトで Next.js なら、まず App Router + RSC で始めて、必要に応じて 「use client」 を貼る」 が、2026年現在の現実的な標準です。 v0 で UI を作る ような AI 連携の文脈でも、「生成された UI は Client、データ取得は Server」 という配置が自然に決まりやすくなります。
AI 時代の RSC
AI を組み込んだアプリで RSC が嬉しい場面はいくつかあります。
ストリーミング応答
AI の応答を サーバで受け取って、必要な単位でクライアントに送る。RSC + Server Actions + Suspense と組み合わせると、「AI が出してきたものを順次表示する UI」 が綺麗に書ける。
バンドルサイズの維持
AI 関連のライブラリは大きくなりがち。「サーバだけで使う AI ライブラリはバンドルに乗らない」 のが、ユーザー体感速度に効く。
「AI が出力 → サーバが整形 → 必要な部分だけ Client で動かす」 という現代的な構造が、RSC によって自然に書けるようになったのが大きな変化です。
React Server Components に関するよくある質問
Q. RSC は SSR とどう違うのですか?
A. SSR は 「初回 HTML をサーバで作る + クライアント側 JS も送る」、RSC は 「クライアントに JS を送らない種類のコンポーネント」 です。並べて使うもので、「SSR + RSC」 が App Router の標準的な構成になっています。
Q. すべてを RSC にすべきですか?
A. いいえ。「動かす部分(ボタン、フォーム、ドラッグ操作など)」 はどうしても Client Components が必要です。葉の部分だけ Client にして、構造は RSC という設計が一番素直に書けます。
Q. RSC からクライアントに 「props」 で何でも渡せますか?
A. シリアライズ可能なものに限定されます。関数 / クラスインスタンス / DOM ノード / Promise(条件付き) は基本的には渡せません。「値や JSX」 は渡せます。
Q. ローカルストレージや Cookie は RSC で扱えますか?
A. localStorage はクライアント専用 なので RSC では触れません。Cookie はサーバで 「cookies()」 API を使えば読めます。これにより 「ログイン中ユーザーで分岐」 のような処理を RSC で完結できます。
Q. RSC でエラーをハンドリングするには?
A. 「error.tsx」 を App Router のルートセグメントに置くと、その配下でエラーが起きたときに表示される Client Component が動きます。「try / catch + JSX」 を直接書くことも可能です。
Q. RSC の本番運用は安全ですか?
A. 2024年〜2025年で大幅に成熟しました。Next.js を本番採用している企業の多くが App Router に移行済みで、本番運用上の問題は概ね小さい 状態です。とはいえ、「古いライブラリの非互換」 等は残るので、移行時には依存の点検が必要です。
Q. RSC は将来的に標準になりますか?
A. React の中核機能としてすでに 「標準」 です。React 19 でリリースされ、Remix(React Router) / Waku など Next.js 以外のフレームワークでも採用が進んでいます。「一過性のトレンド」 ではなく 「React の進化方向そのもの」 と捉えるのが現実的です。
参考リンク
- React 公式: Server Components
- Next.js: App Router 公式
- Next.js: Server and Client Components
- React 公式: 「use client」
- Vercel Blog: Understanding React Server Components
- Remix(React Router): 公式