React Server Componentsとは
React Server Components(RSC)は、ブラウザとは別の環境で先にrenderされるReact Componentです。build時に実行する構成と、requestごとにserverで実行する構成があります。
React 19では、Server Componentsを利用するアプリ向けの機能が安定しました。ただし、RSCをframeworkやbundlerへ組み込むための下位APIはsemverに従わず、React 19.xのminor updateでも変わる可能性があると公式ドキュメントに記載されています。
アプリ開発者は、RSCを自力でbundlerへ組み込むのではなく、Next.js App Routerなど対応frameworkを通して使うのが現実的です。

Server ComponentsとSSRの違い
Server ComponentsとServer-Side Rendering(SSR)は同じではありません。
| 項目 | Server Components | SSR |
|---|---|---|
| 主な目的 | Componentの一部をserver側だけで実行 | 最初に表示するHTMLをserverで生成 |
| browserへComponentコードを送るか | Server Component自体は送らない | hydrateするClient Componentのコードは送る |
| state・event handler | 利用できない | hydrate後のClient Componentで利用 |
| DBへの直接アクセス | server環境なら可能 | server処理内なら可能 |
| 組み合わせ | Client Componentsと組み合わせる | RSCとも組み合わせられる |
Next.js App Routerの初回表示では、RSC PayloadとClient Componentsを使ってHTMLも生成します。そのため、実際のframeworkではRSCとSSRが協調して動く場合があります。
Next.js App Routerでの基本
App Routerでは、pageとlayoutは標準でServer Componentsです。
import { getPost } from "@/lib/posts";
export default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const post = await getPost(id);
return (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
</article>
);
}
Server Componentでは、server側のdata layerへ直接アクセスできます。API keyやDB passwordをbrowser bundleへ含めずに処理できます。
ただし、秘密情報を使えることと、認可が自動的に行われることは別です。利用者がそのデータを読めるか、server側で毎回確認します。
Client Componentが必要な処理
次の処理にはClient Componentを使います。
useStateなどのstate- clickやinputなどのevent handler
useEffectwindow、localStorage、Geolocationなどbrowser API- client専用のcustom hook
ファイル先頭へ"use client"を追加すると、serverとclientのmodule graphの境界になります。
"use client";
import { useState } from "react";
export function LikeButton({ initial }: { initial: number }) {
const [likes, setLikes] = useState(initial);
return (
<button onClick={() => setLikes((value) => value + 1)}>
{likes} likes
</button>
);
}
"use client"を付けたfileがimportするmoduleもclient bundle側へ入ります。page全体へ付けるのではなく、操作が必要な小さな境界へ置くとserver側に残せる範囲が分かりやすくなります。
ServerからClientへ渡せる値
Server ComponentからClient Componentへ渡すpropsは、Reactが転送できる形式でなければなりません。
文字列、数値、boolean、plain object、配列などは扱えます。一方、DB connectionや任意のclass instance、browserへ送れないfunctionを通常のpropsとして渡すことはできません。
import { LikeButton } from "./like-button";
export default async function PostActions() {
const post = await getPostSummary();
return <LikeButton initial={post.likes} />;
}
Server Componentで取得したobject全体を無条件に渡すと、不要な個人情報や内部fieldをRSC Payloadへ含める可能性があります。Client Componentに必要な値だけを明示します。
"use server"の意味
Server Componentを表す"use server" directiveはありません。React公式も、この点をよくある誤解として説明しています。
"use server"は、Client側から呼び出せるServer Functionを定義するためのdirectiveです。form actionとして渡した場合はServer Actionと呼ばれます。
"use server";
export async function updateProfile(formData: FormData) {
const user = await requireUser();
const displayName = String(formData.get("displayName") ?? "");
await saveProfile(user.id, { displayName });
}
Server Functionは公開されたserver endpointと同じように扱います。
- 認証済み利用者を確認する
- 操作対象への認可を確認する
- inputをvalidationする
- CSRFを含むframeworkのsecurity guidanceを確認する
- errorへ秘密情報を含めない
Client Componentから呼べるからといって、安全な内部関数になるわけではありません。
StreamingとSuspense
Server Componentはasync functionにでき、render中にdataを待てます。時間のかかる部分をSuspense境界へ分けると、frameworkは準備できた部分から送信できます。
import { Suspense } from "react";
export default function Page() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>読み込み中...</p>}>
<RecentOrders />
</Suspense>
</main>
);
}
Streamingを使えば必ず指標が一定割合改善するわけではありません。遅いquery、境界の位置、network、cacheによって結果は変わります。
導入時に確認すること
1. client boundaryを広げすぎない
"use client"を上位へ置くほど、client側へ含まれるmoduleが増えます。browser APIが必要なcomponentを切り出します。
2. requestごとの処理を把握する
DB queryや外部APIが、build時、request時、cache hit時のどこで実行されるかを確認します。RSCだけではcache方針は決まりません。
3. server-only dataを渡さない
環境変数、内部ID、権限情報をClient Componentのpropsやerror messageへ含めないようにします。
4. frameworkとReactの組み合わせを固定する
RSCのframework実装APIには互換性上の注意があります。frameworkが対応するReact versionを使い、独自にversionだけ上げない方が安全です。
5. production buildで検証する
development modeとproductionではrender、cache、bundleが異なる場合があります。build、start、実際のdeploy先まで確認します。
段階的に使う
既存のNext.js Pages Routerを一度にApp Routerへ移す必要はありません。URLが衝突しないようにしながら、独立性の高いrouteから移行できます。
- read-onlyなpageを選ぶ
- data取得をServer Componentへ移す
- 操作部分だけClient Componentにする
- loading・error boundaryを追加する
- 認証・認可・cacheを確認する
- bundleとWeb performanceを実測する
RSCの採用を目的にせず、serverでdataへ近づけたい、client JavaScriptを減らしたい、といった具体的な課題があるrouteから始めます。
まとめ
- Server Componentsはbrowserとは別の環境で先にrenderされる
- SSRとRSCは別の概念だが、framework内で組み合わせられる
- stateやbrowser APIにはClient Componentを使う
"use server"はServer ComponentではなくServer Functionのdirective- 認証・認可・validation・cacheは別途設計する
- 固定の性能改善率ではなく、自分のproduction buildで測定する