フレームワーク プログラミング ソフトウェア 公開日 2026.05.15 更新日 2026.05.20

React Server Components(RSC)とは何か?仕組み・Client Components との違い・Next.js App Router での使い方

React Server Components(RSC)は、サーバ側で実行され HTML として返ってくる新しい React コンポーネントの種類です。クライアント JS を出力せず DBAPI を直接叩ける一方で、「useState」 等は使えません。Client Components との違い、「use client」 境界、Next.js App Router での使い方、データ取得・キャッシュの考え方を整理します。

先に要点

  • React Server Components(RSC) は、サーバで実行され、結果(HTML / シリアライズ済みツリー)だけがクライアントに届く 新種のコンポーネント。「使った分のクライアント JS が増えない」 が最大の価値。
  • サーバ専用なので、DB / 内部 API / ファイルシステム / 秘密情報を直接読める 一方、「 useState」 「useEffect」 「onClick」 などの 「状態とブラウザイベント」 系は使えない
  • インタラクション要素は 「 Client Components」(「use client」 ディレクティブを書いたファイル) に切り出す。RSC は 「データを取って木を組み立てる」、Client Components は 「動かす」 という役割分担。
  • 事実上の本命採用先は Next.js App RouterVercel が AI 時代に存在感を増す流れと同じく、バンドル削減 + ストリーミング + サーバ集約 という方向に Web 全体が動いている、その代表格。

Next.js の App Router に切り替えたら 「use client」 って書かないとエラーが出るようになった」 RSC ってよく聞くけど Server Side Rendering とどう違うの? データ取得は 「useEffect + fetch」 でいいのか、それとも 「async コンポーネント」 で書くのか分からない」 ── ReactNext.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 側」。境界が 「ツリーの上から下」 に伝播する。

SSR とは別物

SSR は 「クライアント JS を送りつつ、初回 HTML もサーバで作る」。RSC は 「そもそもクライアント JS を送らない部分を作る」。並べて使える別レイヤー の話。

「RSC = SSR の新版」 ではなく、`SSR の世界に 「クライアント JS を載せないコンポーネント」 という新しいピースを追加したもの」 と捉えるのが正確です。

なぜ RSC が必要になったか

「React と Next.js は何が足りなくて RSC を導入したのか」 を見ると、設計意図が分かります。

①クライアント JS が肥大化する

SPA / SSR の標準では、「画面に必要なコンポーネントすべて」 がバンドルされる。「一度しか使わないテキスト UI」 にも JS のコストがかかる。

② データ取得の二段構え

「 getServerSideProps」 → コンポーネントに props で渡す、という別レイヤーが必要だった。「コンポーネントから直接 DB を呼びたい」 が叶わなかった。

秘密情報がクライアントに漏れる事故

API キーをクライアントに渡してしまう」 系の事故が、「境界が曖昧」 なせいで起きやすかった。「サーバでしか動かない部分」 を構造的に分離したい。

④ ストリーミングを活かしたい

サーバから 「準備できた部分から順番に流す」 ことで体感速度を上げたい。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>
  );
}

注目点

「UserPage」 は 「async」 で DB を直接読む。RSC 上で 「 fetch / SQL / ORM 呼び出しが普通に書ける」 のが App Router の体験。

境界の引き方

「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 が嬉しい場面はいくつかあります。

秘密情報の管理

OpenAI / Anthropic などの API キーは サーバでしか触れない ようにできる。「Client にキーが漏れる」 系の事故を構造的に防げる。

ストリーミング応答

AI の応答を サーバで受け取って、必要な単位でクライアントに送る。RSC + Server Actions + Suspense と組み合わせると、「AI が出してきたものを順次表示する UI」 が綺麗に書ける。

バンドルサイズの維持

AI 関連のライブラリは大きくなりがち。「サーバだけで使う AI ライブラリはバンドルに乗らない」 のが、ユーザー体感速度に効く。

構造化された UI 出力

AI が出した構造化データを Zod で検証 → RSC で JSX に組み立て → 必要な部分だけ Client、という流れは Zod と非常に相性が良い。

「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」 は渡せます。

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 の進化方向そのもの」 と捉えるのが現実的です。

参考リンク

あとで見返すならここで保存

読み終わったあとに残しておきたい記事は、お気に入りからまとめて辿れます。