こんにちは、かつコーチです。
前回はServer Componentsとは何か、これまでのReactとの一番の違いを解説しました。
「インタラクティブな部分は "use client" を使ってClient Componentに切り出す」というところまでは理解できても、実際にどのファイルに、どの粒度で書けばいいか迷う方は多いです。
今回は、"use client" をいつ・どこに書くべきか、判断基準を具体的に解説します。
‘use client’とは?
ファイルの境界を宣言するディレクティブ
"use client"とは、ファイルの一番上に書くディレクティブ(指示文)で、「このファイルとその中で使うコンポーネントはClient Componentとして扱ってください」とNext.jsに伝えるものです。
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
なぜそれが必要なのか
前回解説した通り、useState や onClick はブラウザ側でしか動かないため、それらを使うファイルには明示的に「ここから先はブラウザで動きます」という境界を宣言する必要があります。
この境界がないと、Next.jsはどこまでをサーバー用のバンドルに含め、どこからをブラウザ用のJavaScriptとして出力すればいいか判断できません。
基本の書き方
手順1:’use client’はファイルの一番上に書く
// components/LikeButton.tsx
"use client";
import { useState } from "react";
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? "いいね済み" : "いいね"}
</button>
);
}
import文よりも上に書く必要があり、コメント以外の何かがその前にあるとエラーになります。
手順2:’use client’は「そのファイルからimportされる先」に伝播する
"use client" を書いたファイルからimportされたコンポーネントは、たとえそのファイル自体に "use client" がなくても、Client Componentとして扱われます。
// components/LikeButton.tsx
"use client";
import { Icon } from "./Icon"; // Iconには"use client"がなくてもClientとして動く
export function LikeButton() {
// ...
}
一方、Client Componentの子要素として渡されたServer Componentは、Server Componentのまま維持されます。
// app/page.tsx(Server Component)
import { Modal } from "./Modal"; // Client Component
import { ServerChart } from "./ServerChart"; // Server Component
export default function Page() {
return (
<Modal>
<ServerChart /> {/* childrenとして渡すとServerのまま */}
</Modal>
);
}
判断基準:どこに’use client’を書くべきか
以下のいずれかに当てはまる場合のみ、そのコンポーネントに "use client" を付けます。
useState・useEffect・useContextなどのHookを使うonClick・onChangeなどのイベントハンドラを使うwindow・localStorageなどブラウザ専用APIを使う- サードパーティ製のクライアント向けライブラリを使う
逆に言えば、これらに該当しない限り、"use client" は不要です。
つまずきやすい設定・注意点
"use client" は「そこから下の全てをブラウザ用JavaScriptに含める」宣言なので、ファイルツリーの上のほう(layout.tsx など)に書くと、影響範囲が意図せず広がります。
よくあるつまずきポイント・エラー対処
一次情報:layout.tsxに’use client’を書いてバンドルが膨らんだ話
私が最初に「エラーが出るのが面倒だから」という理由で、app/layout.tsx にまるごと "use client" を書いてしまったことがあります。
❌ Before:layout.tsxに’use client’を書く
// app/layout.tsx
"use client";
import { Header } from "./Header";
import { Footer } from "./Footer";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
一見エラーは出ず動いているように見えたのですが、ビルド後の出力を見て「クライアント側JavaScriptのサイズが想定より大きい」ことに気づきました。
layout.tsx はほぼ全ページで使われる共通の入口のため、ここに "use client" を書くと、配下の多くのコンポーネントが実質的にClient Component扱いになってしまいます。
✅ After:インタラクティブな部分だけを切り出す
// app/layout.tsx("use client"なし、Server Componentのまま)
import { Header } from "./Header";
import { Footer } from "./Footer";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
// app/Header.tsx
"use client";
import { useState } from "react";
export function Header() {
const [open, setOpen] = useState(false);
// ハンバーガーメニューの開閉だけがuse clientの理由
return (
<header>
<button onClick={() => setOpen(!open)}>メニュー</button>
{open && <nav>...</nav>}
</header>
);
}
layout.tsx 自体は "use client" なしのServer Componentに戻し、実際に useState が必要な Header.tsx だけに "use client" を付け直すことで、バンドルサイズを大幅に減らせました。
「"use client" は、必要な葉っぱ(末端のコンポーネント)にだけ付ける」というのが基本方針だと、身をもって学んだ出来事でした。
まとめ
この記事のポイント
"use client"はファイル先頭に書き、そのファイルをClient Componentの境界にする宣言useState・イベントハンドラ・ブラウザAPIを使う場合にのみ必要になる- Client Componentからimportされたコンポーネントは、自動的にClientとして扱われる
- Client Componentの
childrenとして渡したServer Componentは、Serverのまま維持される "use client"は上流のファイルではなく、末端の小さいコンポーネントに付けるのが基本
次に読むべき記事
Server Component・Client Componentの使い分けが分かったところで、次はフォーム送信の新しい書き方を見ていきましょう。
次回は、Server Actionsの基本として、フォーム送信をサーバー側で処理する方法を解説します。
→ 次の記事:Server Actionsの基本:フォーム送信をサーバー側で処理する