【Next.js】’use client’の役割と挙動
【Next.js】’use client’の役割と挙動

【Next.js】’use client’の役割と挙動

ユーザーの操作に応じて画面を更新したり、APIを呼び出したりする場合は、‘use client’ で対応することになりますが、Next.js側でどのように対応しているのかを説明したいと思います。

特にSSGなどでは、正しく‘use client’ を使用しないとビルドエラーにもなりますし、一方で理解不足で使用するとSSG/SSRのメリットを失うこともあるので注意が必要です。

‘use client’ も最初は「サーバーでHTML化」されている

‘use client’ を使用すると、動的な動きができるようになることから、SPAと同じような挙動になるため、jsで画面が作成されると思うかもしれません。しかし、実際はSSG/SSRでもサーバーからHTML化されてファイルが送信されてきます(SSGなら事前生成されたファイルが)。

純粋なReactで作った従来のSPAは、サーバーからは空っぽに近いHTMLと大量のJavaScriptが送られてきて、ブラウザ側でイチから画面を組み立てます。そのため、初期ロード時は画面が真っ白になったり、ローディングスピナーが出たりします(詳しくは後述の比較表を参照)。

「ハイドレーション」の仕組み

裏側では、以下のようなステップが踏まれています。

1. サーバー側で完全なHTMLを作る
Next.jsは、Server Componentだけでなく、Client Componentも一緒に実行して、画面全体の見た目が完成した静的HTMLを作ります(この時点ではボタンを押しても動きません)。

2. ブラウザへHTMLを最速で届ける
ユーザーがアクセスすると、完成済みのHTMLが送られます。JavaScriptの実行を待たずに画面が表示されるため、ユーザー体験が向上し、クローラーも正しくコンテンツを読み取れます。

3. 後からJavaScriptを注入する(ハイドレーション)
画面が表示された直後、ブラウザが裏側で ‘use client’ 用のJavaScriptをダウンロードします。そして、表示されている静的なHTMLに「クリックイベント(onClick)」や「状態管理(useState)」を後付けで結びつけます。

乾いたHTMLに、後からJSの水分を行き渡らせて生き返らせる。この処理をハイドレーション(Hydration)と呼びます。

比較表:何がどう違うのか?

特徴 Server Component (デフォルト) Client Component (‘use client’) 従来のSPA (純粋なReact)
初回のHTML生成 サーバーで行う サーバーで行う ブラウザで行う(初期ロードは空に近いHTML)
JSの送信 クライアント実行用のJSは送られない クライアント側で必要なJSだけ送られる アプリ全体のJSが送られる
Hooks / イベント ❌ 使えない ✅ 使える (useState, onClick等) ✅ 使える

どんな時にビルドエラーになるのか

‘use client’ の付け忘れは、以下のようなケースでビルドエラー(またはランタイムエラー)を引き起こします。

Server Component内でuseStateonClickなどを使ってしまう
Server Componentはサーバー上でのみ実行されるため、ブラウザ側の状態管理やイベントハンドラを持てません。

‘use client’ の付いていないファイルからwindowdocumentなどのブラウザAPIを直接呼び出す
サーバー環境にはwindowが存在しないため、ビルド時または実行時にエラーになります。

// ❌ NG: 'use client' がないのに useState を使用(ビルドエラー)
export default function Counter() {
  const [count, setCount] = useState(0) // Error: useState is not supported in Server Components
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}
// ✅ OK: 'use client' を宣言してからブラウザ機能を使う
'use client'

import { useState } from 'react'

export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

SSGでもSSRでも、ハイドレーションの原則は同じ

「じゃあ、SSG(静的サイト生成)とSSR(サーバーサイドレンダリング)で、‘use client’ の挙動は変わるの?」という疑問が湧くかもしれません。

結論から言うと、SSGでもSSRでも「見た目(HTML)が先、脳みそ(JS)が後」というハイドレーションの流れは全く同じです。

唯一の違いは、「いつそのHTMLを作るか」というタイミングだけです。

  • SSGの場合next build を実行した時(ビルド時)にHTMLを作っておく。いわば「作り置きのお弁当」。
  • SSRの場合:ユーザーがアクセスした時(リクエスト時)に最新データでHTMLを作る。いわば「注文を受けてから作る料理」。

どちらの手法であっても、「Next.jsのサーバー側で一度コンポーネントを実行し、完全なHTMLを作ってからブラウザに渡す」という大原則は揺らぎません。

‘use client’ はどこに配置すべきか

‘use client’ を宣言したからといって、その部分が従来のSPAのように初期ロードで遅延したり、SEOに悪影響を与えたりすることはありません。Next.jsがよしなに事前レンダリングしてくれています。

とはいえ、アプリ全体を ‘use client’ にしてしまうと、送信されるJavaScriptの量が増え、パフォーマンス低下に繋がります。

// ❌ Bad: ページ全体をクライアント化してしまう
'use client'

export default function Page() {
  return (
    <div>
      <StaticHeader />
      <StaticFooter />
      <LikeButton /> {/* 本当に動的なのはここだけ */}
    </div>
  )
}
// ✅ Good: 動的な部分だけを末端コンポーネントとして切り出す
export default function Page() {
  return (
    <div>
      <StaticHeader />
      <StaticFooter />
      <LikeButton /> {/* このファイルの先頭だけに 'use client' を書く */}
    </div>
  )
}

「ユーザーの操作を受け付ける場所」や「状態を持つ場所」の末端(Leaf)にだけ、ピンポイントで‘use client’ を配置する。

これが、Next.jsのパフォーマンスを最大限に引き出すためのベストプラクティスです。

まとめ

  • ‘use client’ を付けても、初回のHTMLは必ずサーバー側で生成される(SPAとは違う)
  • ・表示後にJSが注入されてインタラクティブになる処理を「ハイドレーション」と呼ぶ
  • ・SSG/SSRの違いは「HTMLを作るタイミング」だけで、ハイドレーションの流れ自体は同じ
  • ・Server Component内でuseStateやブラウザAPIを使うとビルドエラーになる
  • ‘use client’ はコンポーネントツリーの末端に、必要な箇所だけピンポイントで配置するのがベストプラクティス

関連記事