本文へ移動
verified onnext@16.3.5react@19
最終検証日 2026-09-26
Next.js道場

useState・useRouter を Server Component で使えない

実際に出るエラー

Error: You're importing a module that depends on `useState` into a React Server
Component module. This API is only available in Client Components. To fix, mark
the file (or its parent) with the `"use client"` directive.

Server Component のファイルから useState などのフックを import したときに出るビルドエラーです。

何が起きているか

App Router のファイルは、既定で Server Component として解決されます。useState はブラウザ側の状態を扱う API なので、Server Component のモジュールからは import できません。

重要なのは、エラーが出るのは「使った場所」ではなく「import したモジュール」だという点です。エラー文も "importing a module that depends on useState" と書いています。自分では useState を書いていなくても、間接的に依存しているものを import しただけで同じエラーになります。

これは描画より前、モジュールを解決する段階で検出されます。だから next build も next dev も、ページを開く前に止まります。

どの段階で止まるか。画面を開く前に分かる
  1. Build
    モジュールを解決する

    import をたどって、そのファイルが何に依存しているかを調べる。ここで止まる。

  2. Server
    (到達しない)サーバーで描画する

    解決の段階で失敗しているので、描画までは進まない。

  3. Browser
    (到達しない)ブラウザで操作できるようにする

    だから「開いたら壊れていた」ではなく、ビルドで気づける。

useState 以外のフックでも同じ文言

useRouter・usePathname のような next/navigation のフックも、useEffect も、Server Component から読み込むと同じ形の文言になります。4つのページで確かめました。

Server Component で読み込んだフックと、文言の中の名前(実機で確かめた)
読み込んだもの文言
useRouter(next/navigation)depends on `useRouter`
usePathname(next/navigation)depends on `usePathname`
useSearchParams(next/navigation)depends on `useSearchParams`
useEffect(react)depends on `useEffect`

ビルドは、4つのファイルの分を1回でまとめて報告しました(failed with 4 errors)。直し方はどれも同じで、フックを使う部品に 'use client' を書きます。useRouter の使い方は useRouter の使い方にまとめています。

原因はほぼこの3つ

1. 'use client' を書き忘れている

いちばん多い形です。

// app/counter/page.tsx
import { useState } from 'react'   // ← 先頭に 'use client' が無い
 
export default function Page() {
  const [n, setN] = useState(0)
  return <button onClick={() => setN(n + 1)}>{n}</button>
}

2. 'use client' の位置が間違っている

ファイルのいちばん先頭でなければ効きません。import の後ろに書いても無効です。コメントより前である必要はありませんが、import 文より前に置いてください。

3. import したライブラリが内部でフックを使っている

自分のコードにフックが1つも無いのに出る場合は、これです。UI ライブラリ、フォームライブラリ、アニメーションライブラリなどが該当します。

最短の確認手順

  1. エラー文に出ているファイル名を開く
  2. 1行目が 'use client' かどうかを見る
  3. 違うなら、そのファイルは Server Component
  4. 自分のコードにフックが無い場合は、import 文を上から順に疑う

直し方

境界は葉に置く。ページ全体をブラウザ側へ移さない
うまくいかない
// page.tsx
'use client'
 
export default async function Page() {
  const items = await db.item.findMany()
  const [asc, setAsc] = useState(true)
}

エラーは消えるが、データ取得までブラウザ側の依存になる。

こう書く
// page.tsx(印を書かない)
export default async function Page() {
  const items = await db.item.findMany()
  return <SortButton items={items} />
}

状態を持つ部分だけを別ファイルに切り出す。取得はサーバーに残る。

推奨:フックを使う部品だけを切り出す

ページ全体に 'use client' を付けるのではなく、状態を持つ部分だけを別ファイルにします。

// components/Counter.tsx
'use client'
 
import { useState } from 'react'
 
export function Counter() {
  const [n, setN] = useState(0)
  return <button onClick={() => setN(n + 1)}>{n}</button>
}
// app/counter/page.tsx(Server Component のまま)
import { Counter } from '@/components/Counter'
 
export default async function Page() {
  const title = await getTitle()   // サーバー側でデータを取れる
  return (
    <>
      <h1>{title}</h1>
      <Counter />
    </>
  )
}

こうすると、ブラウザへ送られる JavaScript はカウンターの分だけで済みます。

ライブラリが原因の場合

そのライブラリを使う部分を、'use client' を書いた薄いファイルで包みます。

// components/Chart.tsx
'use client'
 
export { Chart } from 'some-chart-library'

なぜページ全体に付けてはいけないのか

'use client' は「ここから下をブラウザにも送る」という宣言です。ページの先頭に書くと、そのページが import しているものすべてが対象になります。

  • 表示するだけの部分まで JavaScript がブラウザへ送られる
  • DB やサーバー専用の処理を、そのファイルから直接呼べなくなる

付けるほど重くなるので、置く場所はできるだけ葉に近い側です。

同じ画面でも、印の位置でブラウザへ送るものが変わる
ページの先頭に書く
// app/page.tsx
'use client'
 
import { formatPrice } from '@/lib/format'
 
export default function Page() {
  const [n, setN] = useState(0)
  return <div>{formatPrice(n)}</div>
}

formatPrice の実装まで、そのままブラウザへ運ばれる。

押せる部品だけに書く
// app/page.tsx(印を書かない)
import { formatPrice } from '@/lib/format'
import { Counter } from './Counter'
 
export default function Page() {
  return <Counter label={formatPrice(0)} />
}

整形はサーバーで済ませ、結果だけを渡す。運ばれるのはボタンの分だけ。

開発中に出るのか

出ます。これは描画前の解決段階で検出されるので、next dev でもそのページに移動した時点で止まります。本番だけで出るタイプのエラーではありません。

再発防止

  • フックを使うファイルは、作った瞬間に 'use client' を書く
  • 逆に、'use client' を書いたファイルには「ブラウザが必要な処理」しか置かない
  • 新しいライブラリを入れたら、Server Component から使えるかを最初に確かめる

関連するエラー

window や document を Server Component で参照した場合は、ビルドではなく描画の段階で別のエラーになります。

ReferenceError: window is not defined

こちらは import では検出できないので、実際に描画されるまで出ません。

出典

このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。

理解できたか試す

では、このコードはどうなりますか。

このファイルには use client がありません。どうなりますか。

この問題を解くこのテーマの問題 3 問