Next.js App Routerでのuse clientディレクティブの適切な使用パターン

Next.js App Routerの基本

Next.jsのApp Routerでは、デフォルトでサーバーコンポーネントが採用されています。これは、コンポーネントがサーバー側でレンダリングされ、クライアントに送られるのはHTMLと最小限のJavaScriptのみであることを意味します。これにより、初期ロードの高速化やSEOの向上といったメリットが得られます。

一方、クライアントサイドでインタラクティブな機能(イベントハンドリング、Hooksの使用など)を必要とするコンポーネントは、クライアントコンポーネントとしてマークする必要があります。クライアントコンポーネントを利用するには、ファイルの先頭に "use client" ディレクティブを宣言します。

use clientの適切な使用

"use client" ディレクティブは、どこでも宣言できますが、その使用はクライアントコンポーネントとサーバーコンポーネントの境界に限定することが推奨されます。

use clientの伝播

"use client" が宣言されたファイルは、それ自体がクライアントコンポーネントになるだけでなく、そこからインポートされる全てのコンポーネントも自動的にクライアントコンポーネントとして扱われます。つまり、"use client" は宣言されたファイルから下位のコンポーネントツリー全体に伝播します。

例えば、"use client" が宣言された ParentComponentChildComponent をインポートしている場合、ChildComponent のファイルに "use client" がなくても、ChildComponent はクライアントコンポーネントとしてレンダリングされます。

柔軟性とパフォーマンスの維持

この伝播の特性を理解せずに、不必要に多くのファイルに "use client" を宣言してしまうと、以下のような問題が発生する可能性があります。

  • 不必要なクライアントサイドの処理: 本来サーバーでレンダリングできるはずのコンポーネントまでクライアントコンポーネントとして扱われ、バンドルサイズが増加したり、クライアントサイドでのレンダリング時間が増えたりします。
  • コンポーネントの柔軟性の低下: 汎用性の高いUIコンポーネントを開発する際、そのコンポーネントがサーバーコンポーネントとしてもクライアントコンポーネントとしても利用できる柔軟性が失われます。

結論:境界での宣言

したがって、"use client" は、サーバーコンポーネントからクライアントコンポーネントへの切り替えが必要となる、最も上位のコンポーネントにのみ宣言することが重要です。これにより、コンポーネントの柔軟性を保ちつつ、不必要なクライアントサイドの処理を避け、App Routerのメリットを最大限に活かすことができます。

// app/page.tsx (サーバーコンポーネント)
import ClientComponent from './ClientComponent';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome!</h1>
      <ClientComponent /> {/* ここでクライアントコンポーネントをインポート */}
    </div>
  );
}

// app/ClientComponent.tsx (クライアントコンポーネント)
"use client"; // ここで境界を宣言

import React, { useState } from 'react';

export default function ClientComponent() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      You clicked {count} times.
    </button>
  );
}

参考