Client Component とは何か
Client Component は、ファイルの先頭に use client と書くことでブラウザにも送られる React コンポーネントです。
いつ必要になるのか
判断は単純です。ブラウザにしかないものが必要かどうかだけを見ます。
| 必要なもの | Client にする |
|---|---|
useState useReducer などの状態 | 要る |
useEffect | 要る |
onClick onChange などのイベント | 要る |
window document localStorage | 要る |
| ブラウザ専用のライブラリ | 要る |
| 表示するだけ | 要らない |
| データを取ってきて並べるだけ | 要らない |
迷ったら「押せるか」「変わるか」で考えてください。押せる/変わるならブラウザが必要です。
書き方
ファイルのいちばん上、import より前に一度だけ書きます。
'use client'
import { useState } from 'react'
export function Counter() {
const [n, setN] = useState(0)
return <button onClick={() => setN(n + 1)}>{n}</button>
}
サーバーでも動くことに注意
- Serverサーバーで一度描く
初回表示の HTML を作るため。window も localStorage もここには無い。
- BrowserHTML が表示される
見た目はもう出ている。ただしこの時点では押しても何も起きない。
- BrowserJavaScript を読み込んで hydration
同じ内容をもう一度描き、イベントを繋ぐ。ここで初めて押せるようになる。
名前から「ブラウザだけで動く」と思いがちですが、違います。Client Component も、最初の表示のためにサーバー側で一度実行されます。
- Serverサーバーで実行して HTML を作る
初回表示を速くするため。ここではまだ押せない。
- Browserブラウザへ JavaScript が届く
この部品のコードが、そのままブラウザへ配られる。
- Browserブラウザで実行して繋ぐ(hydration)
同じ内容を描き直し、イベントを接続する。ここで押せるようになる。
だから Client Component の中に window.innerWidth をそのまま書くと、手順1で落ちます。ブラウザ限定の処理は useEffect の中に置いてください。
「Client Component = ブラウザだけ」ではなく「Client Component = ブラウザにも送られる」と覚えるほうが正確です。
サーバーで描いた内容と、ブラウザで描き直した内容が食い違うと、hydration のエラーになります(Hydration とは何か・Hydration failed の直し方)。
境界は下にだけ伝わる
'use client' を書いたファイルから import したものは、すべて Client Component になります。逆はありません。親には影響しません。
越えられる: Page から Panel へ渡した値
越えられない: Panel から上へは広がらない。Page は Server のまま
だから、付ける位置が上へ行くほど、ブラウザへ送る JavaScript が増えます。
大きくなってきたら children で逃がす
Client Component の中に重い表示を抱えたくなったら、import するのではなく children として外から渡します。渡されたものは境界に巻き込まれません。
// app/page.tsx(Server のまま)
export default async function Page() {
const items = await db.item.findMany()
return (
<Panel>
<ItemList items={items} /> {/* Server のまま */}
</Panel>
)
}
Panel の中で import { ItemList } と書くと、その瞬間に ItemList も Client になります。children で渡すか import するかで結果が変わるのがここです。
受け取れる props には制限がある
Server から Client へ渡せるのは、値だけです。
- 渡せる: 文字列・数値・配列・プレーンなオブジェクト・
Date・Map・Set - 渡せない: 関数・クラスのインスタンス
関数を渡すとエラーになるので、ハンドラは Client Component の内側で定義します。
よくある誤解
'use client' を付けたら、そのファイルはブラウザでしか動かない?
動きます。上に書いたとおり、初回はサーバーでも実行されます。
async にできない?
Next.js 16.3.5 ではビルドは通ります。「Client Component は async にできない」と書いてある記事は多いのですが、実際に試すと通ります。ただし読み込み中の表示が扱いにくくなるので、use フックか useEffect を使うほうが素直です。
迷ったら付けておけばいい?
逆です。付けるほどブラウザへ送る JavaScript が増えます。必要なところにだけ付けてください。
出典
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。