Dev Study
← 解説「組み合わせ方 — ServerからClientへpropsを渡す」に戻る

サンプルコードで身につける: 組み合わせ方 — ServerからClientへpropsを渡す

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1文字列のpropsを渡す

Server から Client へ props を渡す、組み合わせの最小形です。受け取る側は普通の React コンポーネントと同じ書き方でよく、「Server がデータを用意し、Client が操作を担当する」分担の出発点になります。

// app/greeting.tsx — Client Component
"use client";
export default function Greeting({ userName }: { userName: string }) {
  return <button onClick={() => alert(userName + " さん、ようこそ")}>あいさつ</button>;
}

// app/page.tsx — Server Component
import Greeting from "./greeting";

export default function HomePage() {
  const userName = "kazu"; // サーバー側で用意した値
  return <Greeting userName={userName} />; // props で渡す
}

2いいねボタンに初期値を渡す

サーバーが用意した初期データ(いいね数)を Client に渡し、以降の操作はブラウザ側で完結させる例です。「最初の値はサーバー、その後の変化はクライアント」という時間軸の分担は、実務の画面のあちこちで登場します。

// app/posts/like-button.tsx — Client Component
"use client";
import { useState } from "react";

export default function LikeButton({ initialCount }: { initialCount: number }) {
  // サーバーから渡された値を初期値にして、以降はブラウザで管理
  const [count, setCount] = useState(initialCount);
  return <button onClick={() => setCount(count + 1)}>いいね {count}</button>;
}

// app/posts/page.tsx — Server Component
import LikeButton from "./like-button";

export default function PostPage() {
  const post = { title: "はじめての投稿", likes: 12 };
  return (
    <article>
      <h1>{post.title}</h1>
      <LikeButton initialCount={post.likes} />
    </article>
  );
}

3propsで渡せる値・渡せない値

Server から Client への props は「シリアライズしてネットワーク越しに送れる値」だけ、という制約を JSON 化で疑似的に確かめる例です。関数を渡そうとしてエラーになったとき、この目安を知っていると原因にすぐたどり着けます。

TypeScript

4childrenでServerの中身を包む

Client Component の children に Server Component の内容をそのまま入れられる、という組み合わせ方の例です。「開閉の動きだけ Client、中の重い表示は Server のまま」にでき、Client 化する範囲を最小限に保つ実務テクニックです。

// app/accordion.tsx — 開閉の動きだけ担当する Client Component
"use client";
import { useState } from "react";

export default function Accordion({ children }: { children: React.ReactNode }) {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <div>
      <button onClick={() => setIsOpen(!isOpen)}>詳細を{isOpen ? "閉じる" : "開く"}</button>
      {isOpen && children} {/* 中身はサーバーで作られたものをそのまま表示 */}
    </div>
  );
}

// app/page.tsx — Server Component
import Accordion from "./accordion";

export default function HomePage() {
  return (
    <Accordion>
      <p>この中身は Server Component のまま。開閉だけが Client の仕事。</p>
    </Accordion>
  );
}

5記事ページの実務的な分担

実務の記事ページを「表示は Server、操作は Client」で分割した全体像の例です。本文やタイトルの表示はサーバーで HTML 化して速く届け、いいねとシェアのボタンだけを小さな Client Component に切り出す、レッスン本文の方針の完成形です。

// app/blog/[id]/page.tsx — ページ全体は Server Component
import LikeButton from "./like-button"; // "use client" の小さな部品
import ShareButton from "./share-button"; // 同上

export default async function PostPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const post = { id, title: "記事 " + id, body: "本文..." , likes: 12 };
  return (
    <article>
      <h1>{post.title}</h1> {/* 表示だけ → Server のまま */}
      <p>{post.body}</p>
      {/* 操作が必要な部分だけ Client に切り出して props を渡す */}
      <LikeButton initialCount={post.likes} />
      <ShareButton postTitle={post.title} />
    </article>
  );
}