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"
が宣言された ParentComponent
が ChildComponent
をインポートしている場合、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>
);
}
参考
- 吉井 健文, 『実践Next.js —— App Routerで進化するWebアプリ開発 エンジニア選書』, 株式会社技術評論社 (2023)
- Next.js Documentation: Server Components
- Next.js Documentation: Client Components