0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js App Routerで"use client"が必要になる場面を最小コードで試してみる

0
Posted at

はじめに

Next.js App Routerを触り始めたとき、最初に混乱しやすいのが "use client" だと思います。

特に、React経験者だと次のように感じやすいです。

  • useState を使ったらエラーになる
  • onClick を書いたらエラーになる
  • windowlocalStorage を使おうとするとエラーになる
  • どのファイルに "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で処理する必要はありません。

一方で、次のような処理はブラウザ側で動く必要があります。

  • ボタンをクリックしたときの処理
  • 入力フォームの状態管理
  • useStateuseEffect
  • windowlocalStorage などのブラウザAPI
  • ドラッグ&ドロップなどのインタラクション

このように、表示やデータ取得を中心とした部分はServer Componentにし、ユーザー操作が必要な部分だけClient Componentにすることで、クライアント側に送るJavaScriptを必要な範囲に抑えやすくなります。

つまり、"use client" は「このコンポーネントはブラウザ側の機能が必要です」とNext.jsに伝えるための境界線のようなものです。

ざっくり結論

App Routerでは、app 配下のコンポーネントは基本的に Server Component として扱われます。

そのため、以下のような クライアント側の機能 を使う場合は、対象のコンポーネントに "use client" が必要です。

  • useState
  • useEffect
  • onClick などのイベントハンドラ
  • window
  • document
  • localStorage
  • ブラウザ上で動くライブラリ

逆に、単に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を作ります。

app/page.tsx
export default function HomePage() {
  return (
    <main>
      <h1>use clientの確認</h1>
      <p>これは表示だけのページです。</p>
    </main>
  );
}

この場合、"use client" は不要です。

状態管理もイベントもブラウザAPIも使っていないため、Server Componentのままで問題ありません。

ケース2:useStateを使うと"use client"が必要

次に、カウンターコンポーネントを作ります。

app/components/Counter.tsx
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 で使います。

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" を追加します。

app/components/Counter.tsx
'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 だけを使う例です。

app/components/AlertButton.tsx
export default function AlertButton() {
  return (
    <button onClick={() => alert('clicked')}>
      alertを表示
    </button>
  );
}

これもエラーになります。

onClick はブラウザ上のユーザー操作に反応する処理なので、Client Componentである必要があります。

修正します。

app/components/AlertButton.tsx
'use client';

export default function AlertButton() {
  return (
    <button onClick={() => alert('clicked')}>
      alertを表示
    </button>
  );
}

useState を使っていなくても、イベントハンドラを使う場合は "use client" が必要です。

ケース4:windowやlocalStorageを使う場合も必要

ブラウザAPIを使う場合も、Client Componentにします。

app/components/StorageButton.tsx
'use client';

export default function StorageButton() {
  const handleClick = () => {
    localStorage.setItem('message', 'hello');
    alert(window.location.href);
  };

  return (
    <button onClick={handleClick}>
      localStorageに保存
    </button>
  );
}

windowlocalStorage はブラウザ上に存在するAPIです。
Server Componentでは使えません。

そのため、このような処理を書くコンポーネントには "use client" が必要です。

ケース5:親ページ全体ではなく、必要な部品だけClient Componentにする

"use client" は、なるべく小さい範囲に付ける方が扱いやすいです。

たとえば、以下のようにページ全体をClient Componentにすることもできます。

app/page.tsx
'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になります。

実務では、次のようにインタラクティブな部分だけを別コンポーネントに切り出す方が分かりやすいです。

app/page.tsx
import Counter from './components/Counter';

export default function HomePage() {
  return (
    <main>
      <h1>use clientの確認</h1>
      <p>このページ自体はServer Componentです。</p>
      <Counter />
    </main>
  );
}
app/components/Counter.tsx
'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の構成がかなり整理しやすくなります。

サンプルコード

この記事で確認したサンプルコードは以下に置いています。

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?