use client はどこに書くのか
use client はファイルの先頭に一度だけ書き、そこから下のツリーを Client Component にするためのディレクティブです。
#何のためのものか
App Router では、書いたコンポーネントは既定で Server Component です。サーバーの上だけで実行され、その JavaScript はブラウザに届きません。
ところが useState も onClick もブラウザがないと動きません。そこで「ここから先はブラウザにも送る」という線を引く必要があります。その線を引くのが use client です。
#どこに書くのか
ファイルのいちばん上、import より前に一度だけ書きます。
'use client'
import { useState } from 'react'
export function Counter() {
const [n, setN] = useState(0)
return <button onClick={() => setN(n + 1)}>{n}</button>
}
このファイルを import した先も、さらにその先も、まとめて Client Component になります。境界は下方向にだけ伝わります。
#よくある誤解
誤解1 付けた方が安全そうだから全部に付ける
逆です。use client は「ブラウザに JavaScript を送る」という宣言なので、付けるほどページは重くなります。
置く場所は、ブラウザの機能が実際に必要な、いちばん葉に近いところです。
誤解2 子に付けたら親も Client になる
なりません。伝わるのは下方向だけです。Server Component が Client Component を呼ぶのは正しい形です。
誤解3 Client Component の中では Server Component を使えない
children として渡せば使えます。
// app/page.tsx(Server Component のまま)
export default async function Page() {
const items = await db.item.findMany()
return (
<Panel>
<ItemList items={items} />
</Panel>
)
}
Panel が Client Component でも、ItemList は Server Component のまま実行されます。Client の内側で import すると Client になりますが、children として受け取る場合は既に描画済みのものを差し込むだけだからです。
#境界を越えられないもの
Server から Client へ渡す props は、シリアライズできる必要があります。
| 渡せる | 渡せない |
|---|---|
| 文字列・数値・真偽値 | 関数 |
| 配列・プレーンオブジェクト | クラスのインスタンス |
| Date・Map・Set | Symbol |
| Server Component の要素(children) | Node.js のオブジェクト |
関数を渡そうとしたときのエラーが、初学者がいちばん最初にぶつかる壁です。
#実務では
EC の商品詳細ページなら、こうなります。
app/products/[id]/page.tsx Server(DB から商品を取得)
├─ ProductInfo Server(ただ表示するだけ)
├─ ReviewList Server(ただ表示するだけ)
└─ AddToCartButton Client('use client' はここだけ)
ページ全体に use client を付けると、商品情報もレビューも全部ブラウザに送られます。カートのボタンだけが Client であるべきです。