🎯 この記事の対象者
- Next.jsは先頭に色々書けるのは知ってる
-
"use client"がクライアント側なのは覚えてる - でも他の書き方って似てるし、どう違うのかうろ覚えだし実はよく分かってない🤔
そんな僕のために書いた記事です。
Next.js (App Router) 「動作場所」5種類まとめ
- Next.jsのApp Routerは先頭の宣言(ディレクティブやインポート)が複数ある
- この宣言で コードの動作場所が、サーバー側かクライアント側かが決まる
- 主要な5種類とそれぞれの役割について整理
💻 1."use client" (Client Components)
-
目的と概要:
- ブラウザ側のユーザー操作(ボタンクリックなど)を記述したい
-
useStateやuseEffectなどのReactフックを使いたい
-
特徴・挙動:
- 初期ページロード時には、実はサーバー側で事前レンダリング(SSR)される
- その後、「RSCペイロード」とコンポーネントのJSとしてクライアントへ送信
- ブラウザ側でJavaScriptがアタッチ(ハイドレーション)されて、初めてクリックなどが可能になる
RSCペイロード(React Server Components Payload)
Reactがクライアント側で画面のツリー構造を構築・更新するための、設計図のような特殊なデータ形式。
🏠 2.なにもなし (Server Components)
-
目的と概要:
- Next.js(App Router)の基本(デフォルト状態)
- ブラウザからのGETやPOSTの結果をサーバー側で作成する
- 検索エンジン(SEO)に完成したページを読み込ませたい
- データベースや外部APIから、安全かつ直接データを取得して画面を作りたい
- 初期状態の画面表示をとにかく高速化したい
-
特徴・挙動:
- サーバー側で処理が完結し、結果(HTMLとRSCペイロード)だけがブラウザに送られる
- ブラウザ側で動くJSコードが一切含まれないため、ページ全体のファイルサイズが劇的に軽くなる
- クライアントバウンダリに注意
-
ここが勘違いポイント:
- 「なにも書かない=古いReact(Pages Router)」ではない
- 「なにも書かない=最新のサーバー専用モード」
クライアントバウンダリ
"use client"のファイルからインポートして使われた場合は、自動的にクライアント側で動くコンポーネントに変化する
⚡ 3."use server" (Server Actions)
-
目的と概要:
- ブラウザから「入力結果(フォーム)送信」など、サーバー側の処理を直接呼び出したい
- submitボタンを押したときの動作
- ブラウザからページ内の一部だけをサーバーから取得して書き換えたい
- RPC(リモートプロシジャーコール)として、fetchも可能(fetchされる側)
- ブラウザから「入力結果(フォーム)送信」など、サーバー側の処理を直接呼び出したい
-
技術的な裏側:
- クライアントコンポーネントから通常の関数呼び出しでサーバーアクションを呼び出せる
- 各関数に対して自動的にエンドポイントが作成される (Next.jsの強み)
- 実際には裏側でそのエンドポイントに対してクライアントからPOSTリクエストが飛んでいる
-
特徴・挙動:
- コンポーネント(画面)ではなく「関数」の先頭(またはファイル全体)につける宣言
- ブラウザ側からは関数の『中身』は見えない(安全に秘匿情報を扱える)
-
ここが勘違いポイント:
- 「Server Components(サーバー側で表示するやつ)」を書きたい時に
"use server"と書くのは間違い -
"use server"はあくまで「サーバー側の関数(アクション)」を呼び出すためのもの
- 「Server Components(サーバー側で表示するやつ)」を書きたい時に
■ 得意先のメンテナンス画面のクライアントコンポーネント
const onSubmit = async (data: 得意先Input) => {
const payload = isEdit ? { ...data, 得意先ID: target?.得意先ID } : data;
const res = await save得意先(payload as 得意先Output, isEdit);
if (res.success) {
toast.success(isEdit ? "更新しました" : "登録しました");
onClose();
} else {
toast.error(res.error || "保存に失敗しました");
}
};
ここでサーバーアクションを呼んでます
サーバーへPOSTされて結果を取得する時間を考えたら
当然非同期処理なので await で呼び出す 必然性がよく分かります
const res = await save得意先(payload as 得意先Output, isEdit);
■ 呼び出されるサーバーアクション
"use server";
export async function save得意先(data: 得意先Input, isEdit: boolean) {
try {
const validated = 得意先Model.parse(data);
(以下省略)
4.import "server-only";
-
目的と概要:
- 絶対にサーバー側でのみ動作させたい
- データベースのパスワードや、サーバー側でしか動かない処理が、誤ってブラウザ側に漏れるのを絶対に防ぎたい
- DBアクセスなど特に秘匿性の高い処理に記述
-
特徴・挙動:
- サーバー側でのみ動作することを強制する「安全装置」
- サーバーアクションやサーバーコンポーネントからのみ呼び出し可能
- クライアント側から呼び出そうとするとビルド時にエラー
5.import "client-only";
-
目的と概要:
- 絶対にブラウザ側でのみ動作させたい
-
windowやlocalStorageなど、ブラウザ(クライアント)にしか存在しない機能を使う処理が、誤ってサーバー側で実行されてクラッシュするのを防ぎたい
-
特徴・挙動:
- クライアント側でのみ動作することを強制する「安全装置」
- クライアントコンポーネントからのみ呼び出し可能
- サーバー側で呼び出そうとするとビルド時にエラー
✅ まとめ
基本は何も書かない。(Server Components)
ブラウザの動きが必要になったら "use client" を足す。
✨これが現代のNext.jsの王道スタイル