claude/skills/nextjs-app-router-guide/SKILL.md
Next.js App RouterでのServer Components / Server Actions / Route Handlers等の使い分けガイド。「Server ComponentとClient Componentの違い」「Server Actionsの使い方」「Server FunctionとServer Actionの違い」「revalidatePath/revalidateTag/updateTagの違い」「use cacheの使い方」「Client側のデータ取得パターン」「useTransition/useFormStatus/useActionState/useOptimistic」などの質問に回答する際に参照する。
npx skillsauth add lilpacy/dotfiles nextjs-app-router-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
flowchart TD
A[何を実現したい?] --> B{UIを描画する?}
B -->|Yes| C{state / handlers / useEffect / browser API が必要?}
C -->|Yes| D["Client Component<br/>(use client)"]
C -->|No| E["Server Component"]
E --> E2{データ取得が必要?}
E2 -->|Yes| E3["Server Componentで fetch / DB / ORM"]
E2 -->|No| E4["そのまま描画"]
D --> D2{データ取得の起点は?}
D2 -->|初回表示 / SSRで足りる| E3
D2 -->|Clientで継続取得したい| D3["use() + ServerからPromiseを渡す<br/>または SWR / React Query"]
B -->|No<br/>関数 / ロジック| F{HTTP endpointとして公開したい?}
F -->|Yes| G["Route Handler<br/>(public endpoint / webhook / any content type)"]
F -->|No| H{Clientから呼ぶ?}
H -->|No| I["server-only関数 / DAL<br/>(import 'server-only')"]
H -->|Yes| J{主目的はmutation?}
J -->|Yes| K["Server Function<br/>(mutation文脈では Server Action)"]
J -->|No| L{小さな対話的read?}
L -->|Yes| M["Server Functionも可<br/>ただし第一選択ではない"]
L -->|No| N["Server Component / Client data fetching / Route Handlerを再検討"]
K --> O{更新後の反映は?}
O -->|現在の画面を再取得| P["refresh()"]
O -->|特定pathを無効化| Q["revalidatePath()"]
O -->|stale-while-revalidate| R["revalidateTag(tag, 'max')"]
O -->|read-your-own-writes| S["updateTag()"]
補足:
client -> use serverのreadはできるが、現行docsの軸は「データ取得はServer Component、mutationはServer Function」。そのためuse serverのreadは可能な選択肢であって、デフォルト推奨ではない。
| Hook | 用途 | 典型例 |
|------|------|--------|
| useTransition | UIをブロックせず非同期処理をTransitionとして流す | onClickからServer Functionを呼ぶ |
| useFormStatus | フォーム送信のpending状態取得 | 二重送信防止、スピナー表示 |
| useActionState | Action結果に基づくstate管理 | バリデーションエラー表示 |
| useOptimistic | 楽観的UI更新 | いいねボタンの即時反映 |
環境(server / browser)ごとの「できること」で分ける。
| 用途 | 選択 |
|------|------|
| state / event handlers / useEffect / browser API | Client Component |
| DB/API近接でのデータ取得、秘密情報利用、JS削減、ストリーミング | Server Component |
App Routerではlayouts/pagesはデフォルトでServer Component。Client Componentが必要な場合のみファイル先頭に'use client'を付ける。
重いUI(チャート、エディタ等)は遅延ロードして初期JSを抑える。ssr: falseはClient Componentでのみ使えるので、呼び出し側もClient Componentにしておく。
'use client'
import dynamic from 'next/dynamic'
const HeavyChart = dynamic(() => import('./Chart'), { ssr: false })
export function DashboardChart() {
return <HeavyChart />
}
'use server'で定義されるのがServer Functionで、そのうちmutation文脈で使う呼び名がServer Action。
'use server')Server Functionは「サーバーで動く非同期関数」で、Clientからnetwork request経由で呼べる。そのためasyncである必要がある。use serverディレクティブは、ファイル先頭に置けばそのファイルのexportをserver-sideとして扱い、関数内に置けばその関数だけをServer Functionとして扱える。
Server Functionは、action / mutation contextではServer Actionとも呼ばれる。<form action={...}>や<button formAction={...}>に渡すと、その呼び出しは自動的にTransition上で扱われ、更新後のUIと新データを1回のserver roundtripで返せる。
Server FunctionはClientから呼べるが、現行docsでは「Server Functions are designed for server-side mutations」と明記されている。Clientは現状、Server Functionを1つずつ順番にdispatchしてawaitするため、Client発のreadをこれで統一するのは第一選択ではない。
readはまず次を優先すると整理しやすい:
use()Server Functionの引数・戻り値はシリアライズ可能である必要がある。return valueはserializeされてClientへ送られるので、UIに必要な最小限だけ返すのがよい。巨大な構造体や内部フィールドつきのDB recordをそのまま返さないようにする。
Clientから呼ぶ必要がないDBアクセスや認可ロジックは、use serverのaction本体に全部書くよりも、**Data Access Layer(DAL)**として切り出すと管理しやすい。import 'server-only'でマークすると、Client側からimportされたときにbuild errorにできる。
「どこから呼ぶか」で制約が変わる。
'use server'を置く'use server'を置くClient ComponentからServer Functionを使うときは、モジュール先頭に'use server'を書いた専用ファイルに置く。Client Component内でServer Functionを直接定義することはできない。
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
// Server Function / Server Action の実装
}
「HTTP APIとしての入出力が必要か」で分ける。
| ユースケース | 選択 | |-------------|------| | ブラウザや他クライアントからHTTPで叩きたい | Route Handler | | Webhook / callback URLを受けたい | Route Handler | | JSON / XML / file / textなど、UI以外のレスポンスを返したい | Route Handler | | フォーム送信・ボタン操作など、UIからの更新 | Server Function / Server Action |
Route Handlerは、Web標準のRequest / Response APIを使うcustom request handlerで、public HTTP endpointとして任意のclientからアクセスできる。
一方、Server Actionは「UIからのmutation」に向く。ただし、「API公開しない = 安全」ではない。Server Function / Server Actionはapplication UI経由だけでなくdirect POSTでも到達可能なので、各action内で認証・認可を必ず検証する。
できる。ただし、まずは第一選択かどうかを見直す。
整理すると次の順で考えるとブレにくい:
use()use serverのServer Functionも可最後の4番も成立するが、現行docsはServer Functionsをserver-side mutations向けと位置づけており、Clientからは現状1件ずつ順次処理される。そのため、「Clientから呼ぶreadだからuse serverが第一選択」ではない、というのが最新の整理。
「UIをブロックせずに、手動のイベントから非同期処理をTransitionとして流したい」とき。
useTransitionはUIの一部をbackgroundでrenderできるReact Hookで、isPendingとstartTransitionを返す。<form action>やformActionはもともとTransitionの文脈に乗るが、onClickやuseEffectからServer Functionを手動で呼ぶときはstartTransitionが役立つ。
'use client'
import { useTransition } from 'react'
import { savePost } from '@/app/actions'
export function SaveButton() {
const [isPending, startTransition] = useTransition()
return (
<button
disabled={isPending}
onClick={() => {
startTransition(async () => {
await savePost()
})
}}
>
{isPending ? '保存中...' : '保存'}
</button>
)
}
「フォーム送信のpending状態」をUIに反映したいとき。
useFormStatusは直近のform submissionのstatus informationを返すHook。
'use client'
import { useFormStatus } from 'react-dom'
function SubmitButton() {
const { pending } = useFormStatus()
return <button disabled={pending}>送信</button>
}
注意: useFormStatusは<form>の中でrenderされるcomponentから呼ぶ必要があり、親<form>のstatusだけを返す。同じcomponent内でrenderした<form>自身のstatusは追えない。
「Actionの結果に基づくstate」を作りたいとき。
useActionStateは、Actionの結果のためのstateを作るHookで、現在state・dispatch関数・pendingを返す。
'use client'
import { useActionState } from 'react'
const initialState = { message: '' }
export function SignupForm() {
const [state, formAction, isPending] = useActionState(
async (_prevState, formData: FormData) => {
const email = formData.get('email')
if (!email) return { message: 'メールアドレスは必須です' }
return { message: '送信しました' }
},
initialState
)
return (
<form action={formAction}>
<input name="email" type="email" />
<button disabled={isPending}>送信</button>
<p>{state.message}</p>
</form>
)
}
バリデーションエラー表示や、Server Actionの結果に応じたUI更新に便利。
「非同期Actionの最中だけ、楽観的なstateを見せたい」とき。
useOptimisticは楽観的にUIを更新するHook。setterはAction / Transitionの中で呼ぶのがポイント。
'use client'
import { useOptimistic, useState, useTransition } from 'react'
import { likePost } from '@/app/actions'
export function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
const [isPending, startTransition] = useTransition()
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(current, delta: number) => current + delta
)
function handleLike() {
startTransition(async () => {
addOptimisticLike(1)
const nextLikes = await likePost()
setLikes(nextLikes)
})
}
return (
<button onClick={handleLike} disabled={isPending}>
{optimisticLikes}
</button>
)
}
「いいね」ボタンやカート追加など、即座にフィードバックを見せたいUIに有効。
「特定pathのcacheをon-demandで無効化したい」とき。
revalidatePathは特定pathのcacheをon-demandでinvalidateできる。Server FunctionsとRoute Handlersで呼べるが、Client Componentsでは呼べない。
挙動の違いに注意:
動的segmentのpattern指定:
revalidatePath('/posts/[id]', 'page')
「tagでまとめてcacheを制御」し、読み取り一貫性の要件で使い分ける。
| 関数 | 特徴 | ユースケース |
|------|------|-------------|
| revalidateTag | revalidateTag(tag, 'max')が推奨。stale-while-revalidateで、次回訪問時にstaleを返しつつ裏でfresh取得 | 更新が多少遅れてもよいコンテンツ |
| updateTag | staleを返さず、次requestでfreshを待つ | read-your-own-writes |
updateTagはServer Actions内でのみ使える点に注意。Route Handlerなど他のcontextではrevalidateTagを使う。
現行docsでは、revalidateTagの第2引数なしはdeprecated。blockingな更新が必要ならupdateTagへ寄せるのが分かりやすい。
tag系APIを効かせるには、読み取り側でtagを付けておく必要がある(例: fetch(..., { next: { tags: ['posts'] } })、または'use cache' + cacheTag('posts')など)。
「CSRF対策(proxy / 別origin経由)」や「送信bodyが大きい」とき。
Server Actions自体はNext.js 14でstableだが、設定オプションは現行docsでもexperimental.serverActions配下にある。
// next.config.js
module.exports = {
experimental: {
serverActions: {
allowedOrigins: ['my-proxy.com', '*.my-proxy.com'],
bodySizeLimit: '2mb',
},
},
}
allowedOriginsはOriginとHostを比較するCSRF対策に追加originを許可する設定。bodySizeLimitのdefaultは1MBで、大きいpayloadを送るなら調整できる。
X-Forwarded-Host)の比較による追加のCSRF対策がある「readではなく、書き込みや副作用を起こす操作」の文脈。
use serverで定義されるサーバー関数の広い呼び名<form action> / formAction / startTransitionなどのaction文脈で使うもの<form action=...>や<button formAction=...>は、自動的にTransitionと統合される。
「route / component / functionをcache可能として扱いたい」とき。
use cacheはCache Components featureのdirectiveで、使うにはnext.config.tsでcacheComponents: trueを有効化する。
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
use cacheはファイル / component / async functionの先頭に置ける。まずは関数単位で使うと理解しやすい。
export async function getCachedData() {
'use cache'
return db.user.findMany()
}
「部分的に先に表示して、残りを後から差し込みたい」とき。
App Routerでは、loading.jsで意味のあるloading UIを作り、route segmentのcontentがstreamingされる間にinstant loading stateを表示できる。
典型例:
loading.jsは同じfolderのpage.jsと子孫を自動で<Suspense>境界に包む。さらに細かく制御したい場合は、自前の<Suspense>境界も追加できる。
「同一request内の重複取得を避けたい」とき。
Reactのcacheはdata fetchやcomputationの結果をmemoizeするAPIで、React Server Componentsでのみ使う。
import { cache } from 'react'
const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } })
})
「layoutとpageが同じdataに触る」「複数componentが同じ参照dataを読む」などで役立つ。cacheはserver requestをまたいで永続化されず、server requestをまたぐとinvalidateされる。
Promise.allなどを使うuseEffect / browser APIが必要なときだけClient Componentuse serverのServer FunctionはClientからも呼べるが、設計の主眼はmutationclient -> use server のreadは可能だが第一選択ではないrefresh / revalidatePath / revalidateTag(tag, 'max') / updateTagを要件で使い分けるimport 'server-only'で守るuseFormStatus / useActionState / useOptimisticを活用loading.js、重複取得はReact.cacheで抑制data-ai
ユーザー向けの回答、要件整理、説明、計画、レビュー、仕様、要約を作成するときに使う。自然言語だけでは条件、状態、多重度、期間、時刻、境界値、制約、推論、計算が曖昧になりうる場合、表、デシジョンテーブル、Mermaid 図、計算式、境界値表で一意化する。推測で補った図表要素は必ず明示する。
tools
Cross-agent messaging via SQLite. Send messages between Claude Code, Codex, Gemini CLI, and other agents. No daemon, no network, no dependencies beyond bash and sqlite3.
development
Anti-AI-slop design skill for greenfield pages, audits, redesigns, and design extraction from URLs or screenshots. Use when the user asks to build a new app or landing page, wants to redesign something, invokes Hallmark by name, or uses audit/redesign/study.
development
Reviews animation and motion code against a high craft bar derived from Emil Kowalski's design engineering philosophy. Default to flagging; approval is earned.