use clientとは?Next.js App RouterのServer Components/Client Componentsの境界設計を徹底解説

use clientディレクティブの意味、Server Componentsとの違い、伝播の仕組み、境界をどこに書くべきかの原則、childrenを使ったComposition Pattern、useState関連のよくあるエラーの対処法までを、React・Next.js公式ドキュメントを根拠に解説する。

Next.js App Routerを触り始めると、必ずと言っていいほど "use client" というディレクティブに出会う。何気なくコンポーネントの先頭に書いて動かしている人も多いはずだが、この一行が実際には何を意味し、どこに書くのが正しいのかを正確に説明できる人は意外と少ない。

本記事では、2026年7月時点のReact公式ドキュメントとNext.js公式ドキュメント(Next.js 16.2系、App Router)を一次情報として、use client の正確な定義から、Server ComponentsとClient Componentsの違い、伝播の仕組み、境界設計の原則、childrenを使ったComposition Pattern、よくあるエラーの対処法までを一通り整理する。

1. use clientとは何か

結論から言うと、"use client" は「クライアントでのみ実行されるコードだ」という宣言ではない。React公式ドキュメントは次のように説明している。

'use client' lets you mark what code runs on the client.

これはサーバーとクライアントのモジュールグラフ(import関係の依存ツリー)の間に境界を引くディレクティブである。ファイルの一番先頭、importより前に書く。

"use client";

import { useState } from "react";

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

Next.js公式ドキュメントも同様に、"use client"Server側のモジュールグラフとClient側のモジュールグラフの境界(boundary)を宣言するものと位置づけている。

よくある誤解: 「use client = クライアントでしか実行されない」ではない

ここが最も誤解されやすいポイントだ。"use client" を付けたコンポーネントは、依然としてサーバー側でも実行される。具体的には、Next.jsのApp Routerでは初回アクセス時にClient ComponentもサーバーサイドでHTMLにプリレンダリング(SSR)され、その後ブラウザ側でハイドレーションされてインタラクティブになる。

つまり "use client" が意味しているのは「このコードはクライアント環境でも動く必要がある(ブラウザAPI・state・イベントハンドラを使う)」ということであり、「サーバーでは一切実行されない」という意味ではない。React公式ドキュメントも、'use client' を持たないコンポーネントがサーバーとクライアントの両方の文脈で評価されうることを明示しており、逆にクライアント境界の中にあるコンポーネントもサーバーで一度レンダリングされてからブラウザに送られる、という双方向の事実を押さえておく必要がある。

2. Server ComponentsとClient Componentsの違い

Next.js App Routerでは、app/ 配下のコンポーネントはデフォルトですべてServer Componentsである。Client Componentsにしたい場合にのみ "use client" を明示的に宣言する。

両者にできること・できないことを整理すると次のようになる。

Server ComponentsClient Components
データベース・ファイルシステムへの直接アクセス×
APIキーやシークレットの安全な保持○(クライアントに送られない)×(バンドルに露出する)
async/await によるコンポーネント内データ取得×(React 19時点では非対応)
useState / useReducer などの状態管理Hooks×
useEffect などのライフサイクル系Hooks×
onClick などのイベントハンドラ×
window / localStorage などブラウザAPI×
React Context の作成・購読×(Provider化不可)
クライアントJSバンドルサイズへの影響なし(サーバーで完結)あり(コード自体が転送される)

Next.js公式ドキュメントは使い分けの指針をこう整理している。Client Componentsが必要なのは state・イベントハンドラ・useEffect などのライフサイクルロジック・localStoragewindow などのブラウザ専用API・カスタムHooksを使うとき。Server Componentsが向いているのは データベースやAPIから直接データを取得するとき、APIキーなどのシークレットをクライアントに晒さず使いたいとき、クライアントに送るJavaScript量を減らしたいとき、そしてFirst Contentful Paint(FCP)を改善しコンテンツを段階的にストリーミングしたいときだ。

「Server Componentsは "use server" で宣言する」という誤解もよく見られるが、これは誤りである。React公式ドキュメントも明言している通り、Server Componentsに対応する専用ディレクティブは存在しないapp/ 配下のコンポーネントは何も書かなければそれがそのままServer Componentであり、"use server" はまったく別物のServer Functions(後述)を宣言するディレクティブである。

3. レンダリングの実際: RSC PayloadとSSR・ハイドレーション

use client の意味を正確に理解するには、Next.jsがサーバー側で何をしているかを知っておく必要がある。Next.js公式ドキュメントは、サーバー側の処理をルートセグメント(レイアウトやページ)単位のチャンクに分けて次のように説明している。

  • Server Components は、React Server Component Payload(以下RSC Payload)という専用のデータ形式にレンダリングされる。
  • Client Components とRSC PayloadはあわせてHTMLへとプリレンダリングされる。

RSC Payloadには次の情報が含まれる。

  • Server Componentsのレンダリング結果
  • Client Componentsをレンダリングすべき位置のプレースホルダと、そのJavaScriptファイルへの参照
  • Server ComponentsからClient Componentsに渡されたprops

初回アクセス時、クライアント側では次の順序で処理が進む。

  1. HTML によって、インタラクティブになる前の高速なプレビューがまず表示される
  2. RSC Payload を使って、Client ComponentsとServer Componentsのツリーが再構築(reconcile)される
  3. JavaScript によってClient Componentsがハイドレーションされ、アプリケーションがインタラクティブになる

ハイドレーションとは、静的なHTMLに対してイベントハンドラを結びつけ、インタラクティブにするReactの処理を指す。2回目以降のナビゲーションでは、RSC Payloadがプリフェッチ・キャッシュされ、Client ComponentsはサーバーでレンダリングされたHTMLなしにクライアント側だけでレンダリングされる。

図1: Server → RSC Payload → HTML/クライアントJSバンドル → ブラウザでのハイドレーションという処理の流れ

RSC PayloadからHTML・ハイドレーションまでの流れ

重要なのは、Client Componentsであっても「サーバーでのレンダリングを経ずにいきなりブラウザで動き始める」わけではないという点だ。use client は実行環境をクライアントに固定するものではなく、そのコンポーネントがクライアント側でも動作可能でなければならない(=ブラウザ実行時のReactツリーに組み込まれる)ことを示す境界の宣言である、という理解が正確だ。

4. use clientの伝播の仕組み

"use client" はどこにでも宣言できるが、その効果は宣言したファイル単体にとどまらない。Next.js公式ドキュメントは次のように明記している。

Once a file is marked with "use client", all of its imports and the components it directly renders are included in the client bundle.

つまり、"use client" を宣言したファイルからインポートされるすべてのコンポーネント・関数・依存モジュールが自動的にクライアントバンドルに含まれる。これが「伝播」と呼ばれる挙動であり、React公式ドキュメントも同様の説明をしている。

As dependencies of RichTextEditor, formatDate and Button will also be evaluated on the client regardless of whether their modules contain a 'use client' directive.

例えば "use client" が宣言された SearchBox コンポーネントが IconInput をインポートしている場合、Icon.tsxInput.tsx 自体に "use client" の宣言がなくても、それらは自動的にクライアントコンポーネントとして扱われる。さらにその先で使われているユーティリティ関数(例えば日付フォーマット関数)まで、クライアント側で評価されるようになる。

図2: use client を宣言した地点から下のインポートツリー全体がクライアントバンドルに取り込まれる

use clientの伝播を示すコンポーネントツリー図

一方で、React公式ドキュメントは次の点も強調している。

When a 'use client' module is imported from another client-rendered module, the directive has no effect.

つまり、すでにクライアント境界の内側にあるモジュールからさらに "use client" 付きのファイルをインポートしても、二重に効果が発生するわけではない。境界は「最初にクライアント化される場所」で一度だけ引かれる。

伝播を理解せずに使うとどうなるか

この伝播の性質を理解しないまま "use client" を不必要に多くのファイルへ付けてしまうと、次のような問題が起きる。

  • 不要なJavaScriptがクライアントに送られる: 本来サーバーで完結できるはずのコンポーネントやロジックまでクライアントバンドルに含まれ、バンドルサイズが増大する
  • サーバーの利点が失われる: データベースアクセスやシークレットの利用など、Server Componentsでしかできないことが、その境界の下では一切できなくなる
  • コンポーネントの再利用性が下がる: 汎用的なUIコンポーネントをServer/Clientのどちらの文脈でも使い回せる柔軟性が失われる

5. どこに書くべきか: 境界設計の原則

伝播の仕組みを踏まえると、"use client" をどこに置くべきかという設計原則が導き出せる。

原則: "use client" は、ツリーの葉(leaf)に近い、最下層のインタラクティブなコンポーネントの境界にだけ宣言する。

言い換えれば、「サーバーからクライアントへの切り替えが必要になる、最も末端(最も下位)のコンポーネント」で境界を引く。ページ全体やレイアウト全体に "use client" を付けるのは、そのツリーの下にあるものすべてを問答無用でクライアント化することを意味するため、基本的には避けるべきパターンだ。

Next.js公式ドキュメントも、UIの大部分を占める静的な要素(ロゴやナビゲーションリンクなど)と、インタラクティブな一部分(検索ボックスなど)が混在するレイアウトを例に、次のような分割を推奨している。

// app/layout.tsx (Server Component のまま)
import Search from "./search"; // Client Component
import Logo from "./logo"; // Server Component

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <>
      <nav>
        <Logo />
        <Search />
      </nav>
      <main>{children}</main>
    </>
  );
}
// app/search.tsx
"use client";

export default function Search() {
  // ...
}

Layout 自体はServer Componentのままにしておき、インタラクティブ性が必要な Search にだけ "use client" を宣言する。これにより Logonav まわりの静的な部分はサーバーで完結し、クライアントに送られるJavaScriptは Search に閉じたものだけになる。

もう一つ重要な指針が、Context Providerの配置だ。テーマ切り替えのようなグローバルな状態をReact Contextで共有したい場合、Contextの作成自体はClient Componentでしか行えない(Server Componentsは非対応)。ただし、そのProviderは children を受け取るだけの薄いClient Componentとして実装し、可能な限りツリーの深い位置に置くべきだとNext.js公式ドキュメントは助言している。

// app/theme-provider.tsx
"use client";

import { createContext } from "react";

export const ThemeContext = createContext({});

export default function ThemeProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}
// app/layout.tsx
import ThemeProvider from "./theme-provider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

公式ドキュメントの表現を借りれば、「Providerはできるだけツリーの深いところでレンダリングする(ThemeProvider<html> 全体ではなく {children} だけをラップしている)べきだ。そうすることで、Next.jsがServer Componentsの静的な部分を最適化しやすくなる」。

6. Server Componentを"クライアントの下"に置くパターン: children Composition

「境界から下はすべてクライアントになる」という伝播のルールには、一見すると避けられない制約があるように見える。クライアントコンポーネントの中に、サーバーでしか実行できない処理を含むコンポーネントを差し込みたい場合はどうすればいいのか。

ここで有効なのが、React・Next.js公式ドキュメントが推奨するComposition Pattern、つまり children (あるいは他のprops)としてServer Componentを渡す方法だ。

アンチパターン: Client Component内でServer Componentをimportする

// app/ui/modal.tsx
"use client";

import Cart from "./cart"; // ここでimportした時点でCartもクライアント化される

export default function Modal() {
  const [isOpen, setIsOpen] = useState(false);
  return isOpen ? (
    <div>
      <Cart />
    </div>
  ) : null;
}

Modal.tsx"use client" が宣言されているため、そこから直接importされている Cart も伝播の対象になり、クライアントバンドルに巻き込まれてしまう。Cart が本来サーバーで完結できるコンポーネント(データベースから商品情報を取得するだけ、など)だったとしても、この書き方では恩恵を受けられない。

推奨パターン: childrenとして受け取る

// app/ui/modal.tsx
"use client";

export default function Modal({ children }: { children: React.ReactNode }) {
  const [isOpen, setIsOpen] = useState(false);
  return isOpen ? <div>{children}</div> : null;
}
// app/page.tsx (Server Component)
import Modal from "./ui/modal";
import Cart from "./ui/cart";

export default function Page() {
  return (
    <Modal>
      <Cart />
    </Modal>
  );
}

Modalchildren というスロットを持つだけのClient Componentにする。呼び出し側の Page(Server Component)が <Modal><Cart /></Modal> という形でServer Componentの Cart を差し込む。

図3: Server Componentをimportする場合とchildrenとして渡す場合の違い

childrenパターンによるServer/Client Componentsの合成比較図

なぜこれで Cart がクライアント化されずに済むのか。Next.js公式ドキュメントは次のように説明している。

This behavior applies to components that are part of the Client Component’s module graph, which includes the modules it imports and the components it renders directly. It does not apply to Server Components passed as children or other props. Those components are not imported into the Client Component’s module graph. They are rendered on the server and passed to the Client Component as rendered output.

つまり children として渡された Cart は、Modal.tsxモジュールグラフ(import関係)には含まれないCart はサーバー側であらかじめレンダリングされ、その"レンダリング済みの出力結果"だけがpropsとしてClient Componentに渡される。RSC Payloadの観点で言えば、Modal が開いているとき、ブラウザはすでにサーバーでレンダリングされた Cart の内容を受け取って表示するだけであり、Cart のコードそのものはクライアントに送られない。

この「サーバー側で先にレンダリングしてから、結果をクライアント境界の内側に差し込む」考え方は、Reactの公式ドキュメントでも次のように説明されている。

In the browser, the Client Components will see output of the Server Components passed as props.

children を使ったスロットパターンは、モーダル・タブ・アコーディオンなど「開閉や表示状態はクライアントで管理したいが、中身はサーバーで取得したデータをそのまま出したい」というケースで特に有効だ。

7. よくあるエラーと対処

「You’re importing a component that needs useState」系のエラー

Server Component内でうっかり useStateuseEffect を使おうとすると、Next.jsは次のようなエラーを出す。

Error: 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.

原因は単純で、そのファイル(またはその祖先のどのファイルにも)"use client" の宣言がないためだ。対処法は、Hooksやイベントハンドラを使っているそのコンポーネント自身(あるいはそれをラップする専用のClient Component)の先頭に "use client" を追加すること。ディレクティブは大文字小文字を区別するため "use client" は正確な表記で書く必要があり、かつファイルの一番最初(importより前)に置く必要がある点にも注意したい。

props のシリアライズ制約

Server ComponentからClient Componentへpropsを渡す場合、そのpropsはシリアライズ可能でなければならない。RSC Payloadはネットワーク越しに送られるデータであるため、関数やクラスインスタンスなどそのまま転送できない値を渡そうとするとエラーになる。

React公式ドキュメントが挙げているサポート対象は、プリミティブ値(文字列・数値・真偽値・null・undefined)、Symbol.for で登録されたシンボル、配列・Map・Set・TypedArrayなどのIterable、Date、プレーンオブジェクト、JSX要素、Promise、Server Functions、そしてServer/Client Componentの要素そのものなどだ。逆に、通常の関数(クライアント側モジュールのものや "use server" が付いたものを除く)やクラス・クラスインスタンス、グローバル登録されていないシンボルは渡せない。

// NG: 関数をそのままpropsで渡そうとするとエラーになる
<ClientComponent onSelect={() => doSomething()} />

// OK: Server Functionとして渡す、またはClient Component側でハンドラを定義する

「use client」と「use server」の混同

"use client""use server" は名前が似ているため混同されやすいが、まったく逆方向のディレクティブだ。React公式ドキュメントは次のように整理している。

'use server' marks server-side functions that can be called from client-side code.

"use server"Server Functions(クライアントから呼び出せるサーバー側の関数、フォーム送信やデータ更新などのミューテーションに使う)を宣言するためのものであり、コンポーネントに付けるものではない。しかも「非同期関数の先頭にしか書けない」「サーバー側のファイルでしか使えない」という制約があり、"use client" のようにコンポーネントの境界を作るものではない。「Server Componentsは "use server" で作る」という理解は誤りであり、Server Componentsに対応する専用のディレクティブはそもそも存在しない、という点をあらためて強調しておきたい。

8. まとめ

  • "use client" は「クライアントでしか動かない」という宣言ではなく、サーバーとクライアントのモジュールグラフの境界を宣言するディレクティブである
  • Client Componentsも初回アクセス時はサーバーでHTMLにプリレンダリングされ、その後ブラウザでハイドレーションされる
  • "use client" を宣言したファイルからインポートされるコンポーネント・関数はすべて自動的にクライアントバンドルに含まれる(伝播)
  • 伝播があるからこそ、"use client"ツリーの葉に近い、インタラクティブ性が必要な最下層の境界にだけ宣言するのが原則
  • Client Componentの内側にServer Componentを差し込みたい場合は、importするのではなく children (またはprops)として渡すComposition Pattern を使うことで、Server Component側のコードをクライアントバンドルに含めずに済む
  • Server ComponentからClient Componentへ渡すpropsはシリアライズ可能な値に限られる
  • "use server""use client" の対義語ではなく、Server Functionsという別概念のためのディレクティブである

よくある質問(FAQ)

use clientを書くとSSRされなくなる?

されない、というのは誤りだ。"use client" を付けたコンポーネントも、初回アクセス時はNext.jsのサーバー側でHTMLにプリレンダリング(SSR)される。"use client" が変えるのは「そのコンポーネントのコードがクライアントバンドルに含まれ、ブラウザ側でも実行・ハイドレーションされるかどうか」であって、サーバー側での初回レンダリングを止めるものではない。

全部use clientにしたら何が悪い?

動作はする。実際、App Routerのすべてのファイルの先頭に "use client" を付ければ、Pages Router時代に近い挙動として動かすこと自体は可能だ。ただし、伝播の仕組みにより下位のコンポーネントもすべてクライアントバンドルに含まれるため、クライアントに送られるJavaScriptの量が増え、データベースアクセスやシークレットの利用などServer Componentsでしかできない最適化の恩恵をまったく受けられなくなる。App Routerの利点を活かすには、必要最小限の境界にとどめることが重要だ。

use clientはどのファイルに書く?

「サーバーからクライアントへ切り替える必要がある、最も下位(末端)のコンポーネント」のファイルに書く。具体的には、useState などのHooksやイベントハンドラ、ブラウザAPIを直接使うコンポーネント自身のファイルの先頭(importより前)に宣言する。レイアウトやページ全体を安易にClient Component化せず、インタラクティブな部分だけを切り出して、そのコンポーネントに "use client" を付けるのが基本方針になる。

‘use server’はuse clientの逆?

厳密には「逆」ではなく「別物」だ。"use client" はコンポーネントやモジュールに付けてサーバー/クライアントの境界を宣言するのに対し、"use server" は非同期関数に付けて**クライアントから呼び出し可能なサーバー側の関数(Server Functions)**を宣言する。Server Componentsを作るためのディレクティブは存在せず、app/ 配下でデフォルトのままにしておけばそれがServer Componentになる。この非対称性を理解しておくと、両者を混同しにくくなる。

関連書籍

App RouterとServer Componentsを体系的に学びたい読者には、本記事の参考文献でもある次の一冊を挙げておく。

実践Next.js — App Routerで進化するWebアプリ開発(吉井健文、技術評論社)

他の分野の定番書は エンジニアにおすすめの技術書10選 にまとめている。

参考文献