ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
この記事の要点
- App Router では、レイアウトもページも既定でサーバーコンポーネント。ブラウザ側で動かしたいものだけを明示的に切り出す。
'use client'はファイル先頭に書く境界の宣言。そのファイルが読み込むモジュールも一緒にクライアント側へ運ばれる。- state・イベント・ブラウザ API が必要ならクライアント、DB アクセスや秘密情報を扱うならサーバー、が基本の判断軸。
- クライアントコンポーネントに
childrenとして渡したサーバーコンポーネントは、サーバー側で描画されたまま差し込まれる。 - 境界をなるべく葉のほうへ寄せるほど、ブラウザに送る JavaScript が減る。
App Router を使い始めて最初にぶつかる壁が、コンポーネントが「どこで実行されるのか」という感覚です。React だけを使ってきた場合、コンポーネントはすべてブラウザで動くものでした。App Router では逆で、何も書かなければサーバーで実行されるのが既定です。この記事では、その境界の引き方と実務上の判断基準を整理します。
1どちらを使うかの判断軸
難しく考える前に、必要な機能から機械的に決められます。
| やりたいこと | 置く場所 |
|---|---|
useState / useReducer で状態を持つ | クライアント |
onClick / onChange などのイベントを扱う | クライアント |
useEffect でライフサイクル処理を書く | クライアント |
window / localStorage などブラウザ API を使う | クライアント |
| データベースや内部 API から直接データを取る | サーバー |
| API キーやトークンを使う | サーバー |
| ブラウザに送る JavaScript を減らしたい | サーバー |
| 初回表示を速くしたい・段階的に流し込みたい | サーバー |
迷ったらサーバー側に置き、必要になった時点で切り出す、という順序が安全です。逆にすると、本来サーバーで完結できた処理までブラウザへ運ばれてしまいます。
2'use client' は境界の宣言
クライアントコンポーネントにするには、ファイルの先頭(インポートより上)に 'use client' と書きます。
// app/ui/counter.tsx
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count} いいね</p>
<button onClick={() => setCount(count + 1)}>押す</button>
</div>
)
}
ここで理解しておきたいのは、これが「このコンポーネントだけをクライアントにする指定」ではなく、サーバー側とクライアント側のモジュールの分かれ目を宣言するものだという点です。'use client' を付けたファイルが import したモジュールと、その中で直接描画しているコンポーネントは、まとめてクライアントのバンドルに含まれます。子コンポーネント一つ一つに書き足す必要はありません。
3境界は葉のほうへ寄せる
典型的な失敗は、レイアウト全体を 'use client' にしてしまうことです。ロゴとナビゲーションは静的なのに、検索ボックスが対話的だという理由だけで全体をクライアント化すると、送信する JavaScript が無駄に増えます。
// app/layout.tsx(サーバーコンポーネントのまま)
import Search from './search' // これだけがクライアント
import Logo from './logo' // サーバーのまま
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Logo />
<Search />
</nav>
<main>{children}</main>
</>
)
}
対話が必要な最小単位だけを別ファイルに切り出し、そのファイルにだけ 'use client' を書く。これが基本の型です。
4データの受け渡し
サーバーからクライアントへは props で値を渡します。ただし React がシリアライズできる値に限られるため、関数やクラスインスタンスはそのまま渡せません。
// app/[id]/page.tsx(サーバー)
import LikeButton from '@/app/ui/like-button'
import { getPost } from '@/lib/data'
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const post = await getPost(id)
return <LikeButton likes={post.likes} />
}
逆向き、つまりクライアントの中でサーバーの処理を呼びたい場合は props ではなく Server Functions を使います。
5入れ子にする — children というスロット
「モーダルの中にサーバーで取得した内容を出したい」というように、クライアントコンポーネントの内側にサーバーコンポーネントを置きたくなることがあります。直接 import すると境界に飲み込まれてしまいますが、children として渡せばサーバー側で描画された結果が差し込まれます。
// app/ui/modal.tsx
'use client'
export default function Modal({ children }: { children: React.ReactNode }) {
return <div className="modal">{children}</div>
}
// app/page.tsx(サーバー)
import Modal from './ui/modal'
import Cart from './ui/cart' // サーバーコンポーネントのまま
export default function Page() {
return (
<Modal>
<Cart />
</Modal>
)
}
Cart は Modal の module graph に入らないため、サーバー側で描画されたまま渡されます。この「スロット」の考え方は、境界を下げるための最も実用的なテクニックです。
6Context プロバイダの置き方
React の Context はサーバーコンポーネントでは使えません。テーマなどの共有状態が必要な場合は、プロバイダをクライアントコンポーネントとして作り、レイアウトから使います。
// 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>
}
このときプロバイダは、できるだけツリーの深い位置に置くのが推奨されています。html 全体ではなく {children} だけを包むようにすると、静的な部分の最適化が効きやすくなります。
7サーバー専用コードを守る
モジュールはサーバーとクライアントの両方から読み込めてしまうため、API キーを含む処理をうっかりクライアントに持ち込む事故が起こり得ます。Next.js では NEXT_PUBLIC_ が付かない環境変数はクライアント側では空文字に置き換えられるので実害は出にくいのですが、意図しない読み込み自体を防ぐには server-only パッケージを使います。
// lib/data.ts
import 'server-only'
export async function getData() {
// process.env.API_KEY を使う処理
}
これをクライアントコンポーネントから読み込もうとすると、ビルド時にエラーになります。逆に window を触るモジュールには client-only を使えます。
8つまずきやすいところ
「
useState が使えない」「window is not defined」「イベントハンドラをサーバーコンポーネントから渡せない」。いずれも 'use client' の付け忘れか、境界の位置が実態と合っていないサインです。まず対話が必要な最小単位を特定 → そこだけ別ファイルに切り出す →
'use client' を付ける → 親はサーバーのまま props を渡す。この順で直すと境界が肥大しません。サードパーティのコンポーネントが 'use client' を持たずに内部で useState を使っている場合も、サーバーコンポーネントから直接使うとエラーになります。その場合は自前のファイルで再エクスポートし、そこに 'use client' を書けば解決します。
境界の感覚がつかめたら、次はサーバー側でどうデータを取るかに進みます。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 Spring Frameworkのアノテーション一覧
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- PHPでのファイルアップロード方法|multipart/form-dataと$_FILES 2026-08-07 17:15:57
- PHP $_SERVERとは|DOCUMENT_ROOT・PHP_SELFなど主な要素 2026-08-07 17:15:57
- Scratchのアカウントの作り方|メールアドレスとユーザー名の登録手順 2026-08-07 17:15:57
- djangoにおけるMVCアプリケーション実装例 2026-08-07 17:15:57
- Bluetoothとは|仕組み・BLEとClassicの違い・用途 2026-08-07 17:15:57
- Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト 2026-08-07 17:15:57
- Java とは?言語仕様・JVM・主要フレームワーク一覧 2026-08-07 17:15:56
- インストール方法(Windows版) 2026-08-07 17:15:56
- APIキー取得方法 2026-08-07 17:15:56
- 【PHPフレームワーク】Laravelの使い方 2026-08-07 17:15:56
- Scratch 作品の公開方法 2026-08-07 17:15:56
- Google OAuth 2.0 認証の実装方法 2026-08-07 17:15:56
- データベースとは?RDB / NoSQL / SQL の基礎 2026-08-07 17:15:56
- インストール(eclipseプラグイン) 2026-08-07 17:15:56
- Spring Frameworkの使い方 2026-08-07 17:15:56
コメントを削除してもよろしいでしょうか?