server-components について
Next.js App RouterでReact Server Componentsを使うためのスキルです。サーバー/クライアントコンポーネントの選択や、データ取得、インタラクション、ストリーミングの構成を支援します。
主な機能
- 01サーバー/クライアントコンポーネントの指針
- 02サーバーからクライアントへのデータ受け渡し
- 03childrenとスロットによる構成
- 04非同期・並列データ取得
- 05Suspenseによるストリーミング
活用シーン
- 01Next.jsアプリのコンポーネント境界の決定
- 02機密情報をサーバーに保ったデータ取得
- 03サーバーレンダリング画面への操作性追加
- 04時間のかかる画面部分のストリーミング
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ davepoon/ buildwithclaude --skill server-componentsインストール後、Claude Code で/server-componentsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/data-fetching-patterns.mdreferences/composition-patterns.mdserver-vs-client.md詳細
Next.js の React Server Components
概要
React Server Components(RSC)を使用すると、コンポーネントをサーバー上でレンダリングできるため、クライアント側の JavaScript を削減し、データに直接アクセスできます。Next.js App Router では、すべてのコンポーネントがデフォルトで Server Components です。
Server Components と Client Components
Server Components(デフォルト)
Server Components はサーバー上でのみ実行されます。
// app/users/page.tsx (Server Component - default)
async function UsersPage() {
const users = await db.user.findMany() // Direct DB access
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
メリット:
- データベースやファイルシステムに直接アクセスできる
- 機密データ(API キー、トークン)をサーバー上に保持できる
- クライアントバンドルのサイズを削減できる
- コード分割が自動で行われる
Client Components
インタラクティブ機能を追加するには、'use client' ディレクティブを追加します。
// components/counter.tsx
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
Client Components を使用する場面:
useState、useEffect、useReducer- イベントハンドラー(
onClick、onChange) - ブラウザー API(
window、document) - 状態を持つカスタムフック
基本的な考え方
コンポーネントツリーには「クライアント境界」があると考えてください。
Server Component (page.tsx)
├── Server Component (header.tsx)
├── Client Component ('use client') ← boundary
│ ├── Client Component (child)
│ └── Client Component (child)
└── Server Component (footer.tsx)
主なルール:
- Server Components は Client Components をインポートできます
- Client Components は Server Components をインポートできません
- Server Components を
childrenとして Client Components に渡すことができます
コンポジションパターン
パターン 1:サーバーのデータ → クライアントのインタラクティブ機能
Server Component でデータを取得し、Client に渡します。
// app/products/page.tsx (Server)
import { ProductList } from './product-list'
export default async function ProductsPage() {
const products = await getProducts()
return <ProductList products={products} />
}
// app/products/product-list.tsx (Client)
'use client'
export function ProductList({ products }: { products: Product[] }) {
const [filter, setFilter] = useState('')
const filtered = products.filter(p =>
p.name.includes(filter)
)
return (
<>
<input onChange={e => setFilter(e.target.value)} />
{filtered.map(p => <ProductCard key={p.id} product={p} />)}
</>
)
}
パターン 2:Server Components を children として渡す
Server Components を children プロパティ経由で渡します。
// components/client-wrapper.tsx
'use client'
export function ClientWrapper({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false)
return (
<div>
<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
{isOpen && children} {/* Server Component content */}
</div>
)
}
// app/page.tsx (Server)
import { ClientWrapper } from '@/components/client-wrapper'
import { ServerContent } from '@/components/server-content'
export default function Page() {
return (
<ClientWrapper>
<ServerContent /> {/* Renders on server! */}
</ClientWrapper>
)
}
パターン 3:複雑なレイアウトにスロットを使用する
複数の children スロットを使用します。
// components/dashboard-shell.tsx
'use client'
interface Props {
sidebar: React.ReactNode
main: React.ReactNode
}
export function DashboardShell({ sidebar, main }: Props) {
const [collapsed, setCollapsed] = useState(false)
return (
<div className="flex">
{!collapsed && <aside>{sidebar}</aside>}
<main>{main}</main>
</div>
)
}
データ取得
非同期 Server Components
Server Components は非同期にできます。
// app/posts/page.tsx
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts')
.then(res => res.json())
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
並列データ取得
複数のリソースを並列で取得します。
export default async function DashboardPage() {
const [user, posts, analytics] = await Promise.all([
getUser(),
getPosts(),
getAnalytics(),
])
return (
<Dashboard user={user} posts={posts} analytics={analytics} />
)
}
Suspense によるストリーミング
時間のかかるコンポーネントをストリーミングします。
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Header /> {/* Renders immediately */}
<Suspense fallback={<PostsSkeleton />}>
<SlowPosts /> {/* Streams when ready */}
</Suspense>
</div>
)
}
選択ガイド
Server Component を使用する場面:
- データを取得する場合
- バックエンドリソースにアクセスする場合
- 機密情報をサーバー上に保持する場合
- クライアント側の JavaScript を削減する場合
- コンポーネントにインタラクティブ機能がない場合
Client Component を使用する場面:
- 状態を使用する場合(
useState、useReducer) - エフェクトを使用する場合(
useEffect) - イベントリスナーを使用する場合
- ブラウザー API を使用する場合
- 状態を持つカスタムフックを使用する場合
よくある間違い
- 避けること: 不必要に
'use client'を追加すること。バンドルサイズが増加します - 避けること: Client Components に Server Components をインポートしようとすること
- 実施すること: 境界でデータをシリアライズすること(関数、クラス、日付は不可)
- 実施すること: コンポジションに children パターンを使用すること
リソース
詳細なパターンについては、以下を参照してください。
references/server-vs-client.md- 完全な比較ガイドreferences/composition-patterns.md- 高度なコンポジションexamples/data-fetching-patterns.md- データ取得の例