mcpField
S
server-components

server-components

github.com/davepoon開発3.7k152 回インストール今日更新
リポジトリ

server-components について

Next.js App RouterでReact Server Componentsを使うためのスキルです。サーバー/クライアントコンポーネントの選択や、データ取得、インタラクション、ストリーミングの構成を支援します。

主な機能

  1. 01サーバー/クライアントコンポーネントの指針
  2. 02サーバーからクライアントへのデータ受け渡し
  3. 03childrenとスロットによる構成
  4. 04非同期・並列データ取得
  5. 05Suspenseによるストリーミング

活用シーン

  1. 01Next.jsアプリのコンポーネント境界の決定
  2. 02機密情報をサーバーに保ったデータ取得
  3. 03サーバーレンダリング画面への操作性追加
  4. 04時間のかかる画面部分のストリーミング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/davepoon/buildwithclaude --skill server-components

インストール後、Claude Code で/server-componentsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
data-fetching-patterns.md
references/
composition-patterns.md
server-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)

主なルール:

  1. Server Components は Client Components をインポートできます
  2. Client Components は Server Components をインポートできません
  3. 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 を使用する場合
  • 状態を持つカスタムフックを使用する場合

よくある間違い

  1. 避けること: 不必要に 'use client' を追加すること。バンドルサイズが増加します
  2. 避けること: Client Components に Server Components をインポートしようとすること
  3. 実施すること: 境界でデータをシリアライズすること(関数、クラス、日付は不可)
  4. 実施すること: コンポジションに children パターンを使用すること

リソース

詳細なパターンについては、以下を参照してください。

  • references/server-vs-client.md - 完全な比較ガイド
  • references/composition-patterns.md - 高度なコンポジション
  • examples/data-fetching-patterns.md - データ取得の例

コメント