Next.js App Router で useState が使えないエラーが解消できない
App Router は既定でサーバーコンポーネント。
`useState` などのフックや `onClick` を使うファイルは先頭に `use client` ディレクティブが必要。
書き忘れ・位置ミス・境界設計の不一致が主因。
#nextjs#app-router#use-client#server-component#hooks
公開:
要約
App Router でフックが使えないエラーが出る場合、原因はほぼ「use client ディレクティブを書いていない / 位置が誤っている」。
サーバーコンポーネントが既定で、フックや onClick などのイベントハンドラはクライアント側でしか動かない。
よくある原因
use client書き忘れ: 「You're importing a component that needs useState. It only works in a Client Component.」エラーが出る- ディレクティブの位置が誤り: import の下に書くと無効になり、ビルドエラーになる
- 境界設計のミス: 親をクライアント化して、本来サーバーで実行したい子コンポーネントまで巻き込んでいる
- クライアント専用ライブラリ:
windowなどブラウザ API に依存する SDK をサーバー側で評価して落ちる
解決策
1. ディレクティブを正しく書く
公式の Client Components ドキュメント のとおり、ファイル先頭に書く。
"use client";
import { useState } from "react";
export function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}2. 境界を細かく分ける
page.tsx 自体はサーバーコンポーネントのままにし、インタラクション部分のみ別ファイル(例: Counter.tsx)に切り出して "use client"; を付ける。
データ取得はサーバーコンポーネント側のガイド に従いサーバー側で完結させると配信量も減る。
3. クライアント専用ライブラリを動的読み込み
import dynamic from "next/dynamic";
const Editor = dynamic(() => import("./Editor"), { ssr: false });ブラウザ API に依存するライブラリはこれでサーバー側評価を回避できる。use client を付けたファイルから動的 import するのが定石。