MeWrite Docs

You're importing a component that needs useState/useEffect

Next.js App RouterでクライアントコンポーネントにuseClientディレクティブがない場合のエラー

概要

Next.js 13以降のApp Routerで、useStateuseEffectなどのクライアントサイドのHooksを使用しているコンポーネントに'use client'ディレクティブがない場合に発生するエラーです。

エラーメッセージ

You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

You're importing a component that imports client-only code. It only works in a Client Component.

原因

  1. use clientの記述忘れ: クライアントコンポーネントに'use client'がない
  2. Hooksの使用: Server ComponentでuseState/useEffectを使用
  3. ブラウザAPI: window/documentなどのブラウザAPIを使用
  4. サードパーティライブラリ: クライアント専用ライブラリをServer Componentでインポート

解決策

1. ‘use client’ ディレクティブを追加

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Good: ファイルの先頭に追加
'use client';

import { useState, useEffect } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

2. コンポーネントを分離

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ServerComponent.tsx (Server Component)
import ClientCounter from './ClientCounter';

export default function Page() {
  // サーバーサイドのデータ取得
  const data = await fetchData();

  return (
    <div>
      <h1>{data.title}</h1>
      <ClientCounter initialCount={data.count} />
    </div>
  );
}

// ClientCounter.tsx (Client Component)
'use client';

import { useState } from 'react';

export default function ClientCounter({ initialCount }: { initialCount: number }) {
  const [count, setCount] = useState(initialCount);

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

3. イベントハンドラの分離

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// SearchButton.tsx
'use client';

export default function SearchButton() {
  const handleClick = () => {
    // クライアントサイドの処理
    alert('Clicked!');
  };

  return <button onClick={handleClick}>Search</button>;
}

// page.tsx (Server Component)
import SearchButton from './SearchButton';

export default function Page() {
  return (
    <div>
      <h1>Search Page</h1>
      <SearchButton />
    </div>
  );
}

4. ブラウザAPIの使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
'use client';

import { useEffect, useState } from 'react';

export default function WindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });

  useEffect(() => {
    // クライアントサイドでのみ実行
    setSize({
      width: window.innerWidth,
      height: window.innerHeight
    });

    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return <p>Window: {size.width} x {size.height}</p>;
}

5. サードパーティライブラリのラップ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// ChartWrapper.tsx
'use client';

import dynamic from 'next/dynamic';

// SSRを無効化してインポート
const Chart = dynamic(() => import('chart.js'), { ssr: false });

export default function ChartWrapper({ data }) {
  return <Chart data={data} />;
}

6. Contextの使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// providers.tsx
'use client';

import { ThemeProvider } from 'next-themes';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system">
      {children}
    </ThemeProvider>
  );
}

// layout.tsx (Server Component)
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

7. フォームの処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// SubmitButton.tsx
'use client';

import { useFormStatus } from 'react-dom';

export function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Submitting...' : 'Submit'}
    </button>
  );
}

// page.tsx (Server Component)
import { SubmitButton } from './SubmitButton';

export default function Page() {
  async function handleSubmit(formData: FormData) {
    'use server';
    // サーバーアクション
  }

  return (
    <form action={handleSubmit}>
      <input name="email" type="email" />
      <SubmitButton />
    </form>
  );
}

Server vs Client Component

機能Server ComponentClient Component
データフェッチ✅ async/await❌ useEffect
useState/useEffect
ブラウザAPI
イベントハンドラ
バンドルサイズ小さい大きい

よくある間違い

  • すべてのコンポーネントに'use client'を付ける(パフォーマンス低下)
  • Server Componentで直接onClickを使う
  • 'use client'をファイルの途中に書く(先頭に書く必要がある)

関連エラー

参考リンク

Next.js の他のエラー

最終更新: 2025-12-13