Open1
React Server Componentsについて

参考:
React Server Components(RSC)の使用タイミングを理解するには、「どんな処理をサーバーで行うべきか」がポイントになります。
以下に、具体的なユースケースとコード例を交えて解説します。
✅ Server Component を使うべきタイミングとコード例
① データベースに直接アクセスしたいとき
サーバーでしか扱えないDB(Prisma、MongoDBなど)とのやりとりはServer Componentで行う。
✅ コード例(Next.js App Routerでの使用)
app/posts/page.tsx
(Server Component)
// このファイルは自動的に Server Component になる(.tsx + app router)
import { prisma } from '@/lib/prisma';
export default async function PostsPage() {
const posts = await prisma.post.findMany(); // サーバーでDBアクセス
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
🧠 ポイント: クライアントでは直接Prismaを使えない(セキュリティ上の問題)ため、Server Component内でのみ使用する。
② セキュアな情報を扱うとき(例:トークンやAPIキー)
✅ コード例(OpenAI APIなど)
// app/chat/page.tsx
import { getChatResponse } from '@/lib/openai';
export default async function ChatPage() {
const response = await getChatResponse("こんにちは!");
return <p>AIの返答: {response}</p>;
}
// lib/openai.ts
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
export async function getChatResponse(prompt: string): Promise<string> {
const res = await openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
});
return res.choices[0].message.content ?? '';
}
🧠 ポイント: APIキーをクライアントに渡さず、Server Componentで呼び出す。
③ SEOが重要なコンテンツ(SSRが望ましい)
✅ コード例(ブログ記事の詳細)
// app/blog/[slug]/page.tsx
import { getPostBySlug } from '@/lib/posts';
export default async function BlogDetail({ params }: { params: { slug: string } }) {
const post = await getPostBySlug(params.slug);
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
🧠 ポイント: ページアクセス時にSSRで生成され、SEOにも有利。
❌ Server Componentに向かない処理
以下は必ず Client Component で行う必要があります:
処理 | 理由 |
---|---|
ユーザーのインタラクション(ボタン、フォームなど) | サーバーではイベントリスナーを持てない |
useState , useEffect , useRef などReact Hooksの使用 |
Server Componentでは使用できない |
ローカルストレージやCookie(ブラウザ側)の読み取り | サーバーにはブラウザ環境がないため |
🔁 Client Componentとして分離する例:
'use client';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? '❤️ Liked' : '🤍 Like'}
</button>
);
}
これをServer Componentに埋め込む:
import { LikeButton } from './LikeButton';
export default async function PostCard() {
return (
<div>
<h2>記事タイトル</h2>
<LikeButton /> {/* クライアントで動作 */}
</div>
);
}
🎯 まとめ:使い分けの指針
処理内容 | 使用すべきコンポーネント |
---|---|
データ取得・DBアクセス | ✅ Server Component |
秘匿情報の操作 | ✅ Server Component |
SEOが重要なページ表示 | ✅ Server Component |
ユーザー操作(ボタン・入力) | ❌ Client Component |
状態管理やアニメーション | ❌ Client Component |
✅ あなたの理解の再確認
「初回のデータフェッチから値が更新されないコンポーネントは Server Component にして、イベントや更新処理があるものは Client Component にする」
これは以下のように整理できます:
特性 | Server Component(SC) | Client Component(CC) |
---|---|---|
初回レンダリングのみで十分 | ✅ 向いている | ❌ 過剰 |
イベントハンドラ(onClick など) | ❌ 使えない | ✅ 必須 |
状態管理(useState/useEffect) | ❌ 不可 | ✅ 必須 |
セキュアなデータ(DB/APIキー)取得 | ✅ 安全 | ❌ 不適切 |
ページ表示や静的レンダリング | ✅ 高速かつ効率的 | ❌ 重い場合も |
🎯 よくあるパターン:SC + CC のハイブリッド例
// Server Component
import { getUser } from '@/lib/db';
import { FollowButton } from './FollowButton';
export default async function UserCard({ userId }: { userId: string }) {
const user = await getUser(userId); // SC で DB 取得
return (
<div>
<h2>{user.name}</h2>
<p>{user.bio}</p>
<FollowButton userId={user.id} /> {/* CC */}
</div>
);
}
// FollowButton.tsx (Client Component)
'use client';
import { useState } from 'react';
export function FollowButton({ userId }: { userId: string }) {
const [isFollowing, setIsFollowing] = useState(false);
async function handleClick() {
await fetch(`/api/follow`, {
method: 'POST',
body: JSON.stringify({ userId }),
});
setIsFollowing(true);
}
return (
<button onClick={handleClick}>
{isFollowing ? 'Following' : 'Follow'}
</button>
);
}
🧠 補足:SCとCCを切り分ける理由
- SCで軽量・高速な初期描画を実現
- CCは必要な箇所だけに限定し、クライアントのJS負荷を削減
- セキュリティ(APIキー露出防止)やパフォーマンス最適化に寄与
✅ 結論
はい、**「初回表示だけで完結する部分はSC、動的・双方向な部分はCC」**という考え方は、まさにReact Server Componentsの基本戦略であり、Next.js 13/14で推奨される設計方針でもあります。