はじめに
Next.js App Routerを触り始めたとき、最初に混乱しやすいのが "use client" だと思います。
特に、React経験者だと次のように感じやすいです。
-
useStateを使ったらエラーになる -
onClickを書いたらエラーになる -
windowやlocalStorageを使おうとするとエラーになる - どのファイルに
"use client"を書けばいいのか迷う - とりあえず全部に
"use client"を付けたくなる
この記事では、Next.js App Routerで "use client" が必要になる場面を、最小コードで確認します。
前提
この記事は、以下の環境を想定しています。
Node.js: 24.13
npm: 11.6
Next.js: 16.2
React: 19.2
TypeScript: ^5
OS: Windows 11
そもそも、なぜServer ComponentとClient Componentを分けるのか
App Routerでは、コンポーネントを大きく分けると Server Component と Client Component があります。
最初は少し分かりにくいですが、ざっくり言うと、サーバーでできることはサーバーで行い、ブラウザでしかできないことだけをクライアント側に寄せるための仕組みです。
たとえば、単にデータを取得してHTMLを表示するだけなら、必ずしもブラウザ側のJavaScriptで処理する必要はありません。
一方で、次のような処理はブラウザ側で動く必要があります。
- ボタンをクリックしたときの処理
- 入力フォームの状態管理
-
useStateやuseEffect -
windowやlocalStorageなどのブラウザAPI - ドラッグ&ドロップなどのインタラクション
このように、表示やデータ取得を中心とした部分はServer Componentにし、ユーザー操作が必要な部分だけClient Componentにすることで、クライアント側に送るJavaScriptを必要な範囲に抑えやすくなります。
つまり、"use client" は「このコンポーネントはブラウザ側の機能が必要です」とNext.jsに伝えるための境界線のようなものです。
ざっくり結論
App Routerでは、app 配下のコンポーネントは基本的に Server Component として扱われます。
そのため、以下のような クライアント側の機能 を使う場合は、対象のコンポーネントに "use client" が必要です。
useStateuseEffect-
onClickなどのイベントハンドラ windowdocumentlocalStorage- ブラウザ上で動くライブラリ
逆に、単にHTMLを表示するだけなら "use client" は不要です。
検証用プロジェクトを作る
まず、Next.jsプロジェクトを作成します。
npx create-next-app@latest next-app
質問は好みに合わせて選択します。
この記事では、以下のような構成を想定します。
TypeScript: Yes
ESLint: Yes
Tailwind CSS: No
src directory: No
App Router: Yes
Turbopack: Yes
import alias: Yes
作成後、起動します。
cd next-app
npm run dev
ブラウザで以下を開きます。
http://localhost:3000
ケース1:表示だけなら"use client"は不要
まずは、通常のServer Componentを作ります。
export default function HomePage() {
return (
<main>
<h1>use clientの確認</h1>
<p>これは表示だけのページです。</p>
</main>
);
}
この場合、"use client" は不要です。
状態管理もイベントもブラウザAPIも使っていないため、Server Componentのままで問題ありません。
ケース2:useStateを使うと"use client"が必要
次に、カウンターコンポーネントを作ります。
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((current) => current + 1)}>
Count: {count}
</button>
);
}
これを app/page.tsx で使います。
import Counter from './components/Counter';
export default function HomePage() {
return (
<main>
<h1>use clientの確認</h1>
<Counter />
</main>
);
}
このままだと、useState を使っているためエラーになります。
修正するには、Counter.tsx の先頭に "use client" を追加します。
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((current) => current + 1)}>
Count: {count}
</button>
);
}
これでボタンをクリックすると、カウントが増えるようになります。
ポイントは、page.tsx ではなく、useState を使っている Counter.tsx に "use client" を付けることです。
ケース3:onClickだけでも"use client"が必要
次に、状態管理なしで onClick だけを使う例です。
export default function AlertButton() {
return (
<button onClick={() => alert('clicked')}>
alertを表示
</button>
);
}
これもエラーになります。
onClick はブラウザ上のユーザー操作に反応する処理なので、Client Componentである必要があります。
修正します。
'use client';
export default function AlertButton() {
return (
<button onClick={() => alert('clicked')}>
alertを表示
</button>
);
}
useState を使っていなくても、イベントハンドラを使う場合は "use client" が必要です。
ケース4:windowやlocalStorageを使う場合も必要
ブラウザAPIを使う場合も、Client Componentにします。
'use client';
export default function StorageButton() {
const handleClick = () => {
localStorage.setItem('message', 'hello');
alert(window.location.href);
};
return (
<button onClick={handleClick}>
localStorageに保存
</button>
);
}
window や localStorage はブラウザ上に存在するAPIです。
Server Componentでは使えません。
そのため、このような処理を書くコンポーネントには "use client" が必要です。
ケース5:親ページ全体ではなく、必要な部品だけClient Componentにする
"use client" は、なるべく小さい範囲に付ける方が扱いやすいです。
たとえば、以下のようにページ全体をClient Componentにすることもできます。
'use client';
import { useState } from 'react';
export default function HomePage() {
const [count, setCount] = useState(0);
return (
<main>
<h1>use clientの確認</h1>
<button onClick={() => setCount((current) => current + 1)}>
Count: {count}
</button>
</main>
);
}
ただし、ページ全体がClient Componentになります。
実務では、次のようにインタラクティブな部分だけを別コンポーネントに切り出す方が分かりやすいです。
import Counter from './components/Counter';
export default function HomePage() {
return (
<main>
<h1>use clientの確認</h1>
<p>このページ自体はServer Componentです。</p>
<Counter />
</main>
);
}
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((current) => current + 1)}>
Count: {count}
</button>
);
}
この形にすると、ページ本体はServer Componentのまま、必要な部分だけClient Componentにできます。
よくある勘違い
"use client"は「ブラウザでだけ描画する」という意味ではない
"use client" を付けると、完全にサーバー側で描画されなくなる、という理解は少し雑かもしれません。
実務で最初に理解するなら、まずは次のように捉えると分かりやすいです。
"use client"は、クライアント側の機能を使うコンポーネントの境界を宣言するもの
つまり、「このファイルから先はClient Componentとして扱う」という目印です。
全部に"use client"を付ければいいわけではない
エラーが出るたびに全部のファイルへ "use client" を付けると、Server Componentのメリットを活かしにくくなります。
基本方針としては、以下で考えるとよいです。
| やりたいこと | use client |
|---|---|
| ただ表示する | 不要 |
| propsを受け取って表示する | 基本不要 |
useState を使う |
必要 |
useEffect を使う |
必要 |
onClick を使う |
必要 |
window を使う |
必要 |
localStorage を使う |
必要 |
| サーバー側でデータ取得する | 不要 |
| DBや秘密情報を扱う | Client Componentにしない |
検証チェックリスト
この記事のコードを試す場合は、以下を確認します。
-
npm run devで起動できるか - 表示だけの
page.tsxは"use client"なしで動くか -
useStateを使うとエラーになるか -
Counter.tsxに"use client"を追加すると動くか -
onClickだけでも"use client"が必要になるか -
localStorageを使う処理がClient Component内にあるか - ページ全体ではなく、部品だけClient Componentにできているか
まとめ
Next.js App Routerでは、まずServer Componentが基本です。
そのうえで、以下が必要になったら "use client" を付けます。
- 状態管理
- イベントハンドラ
- ブラウザAPI
- クライアント側で動くライブラリ
最初は、次の考え方で十分だと思います。
画面全体に
"use client"を付けるのではなく、必要な部品だけClient Componentにする
この感覚が分かると、App Routerの構成がかなり整理しやすくなります。
サンプルコード
この記事で確認したサンプルコードは以下に置いています。
参考
-
Next.js Docs - use client
https://nextjs.org/docs/app/api-reference/directives/use-client -
Next.js Docs - Server and Client Components
https://nextjs.org/docs/app/getting-started/server-and-client-components -
Next.js Learn - Server and Client Components
https://nextjs.org/learn/react-foundations/server-and-client-components