nextjs について
nextjs は Next.js 16 の App Router を使う開発者向けのスキルです。ルーティング、キャッシュ、サーバーコンポーネント、移行時の変更、React 19.2、パフォーマンス、一般的なエラーを扱います。
主な機能
- 01App Router のルーティングとレイアウトパターン
- 02Server Components と Server Actions
- 03Cache Components とキャッシュ API
- 04Next.js 15 から 16 への移行ガイド
- 05メタデータ・画像・フォントの最適化
- 06一般的なエラーとテンプレートの参照
活用シーン
- 01Next.js 16 App Router でページを構築
- 02サーバー側データ取得とフォーム更新の実装
- 03キャッシュと部分プリレンダリングの設定
- 04Next.js 15 プロジェクトの 16 への移行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill nextjsインストール後、Claude Code で/nextjsを入力して読み込まれたか確認します。
同梱ファイルREADME.mdSKILL.json
詳細
Next.js App Router - 本番環境向けパターン
バージョン: Next.js 16.0.0 React のバージョン: 19.2.0 Node.js: 20.9+ 最終確認日: 2025-10-24
目次
- このスキルを使用する場合
- このスキルを使用しない場合
- Next.js 16 の破壊的変更
- キャッシュコンポーネントとキャッシュ API
- サーバーコンポーネント
- サーバーアクション
- ルートハンドラー
- Proxy と Middleware の比較
- 並列ルートとルートグループ
- React 19.2 の機能
- Metadata API
- 画像とフォントの最適化
- パフォーマンスパターン
- TypeScript の設定
- よくあるエラーと解決策
- テンプレートリファレンス
- 追加リソース
このスキルを使用する場合
次のようなニーズがある場合に、このスキルを使用してください。
- Next.js 16 App Router のパターン(レイアウト、読み込み、エラーバウンダリー、ルーティング)
- サーバーコンポーネントのベストプラクティス(データ取得、コンポジション、ストリーミング)
- サーバーアクションのパターン(フォーム、変更、再検証、エラー処理)
"use cache"ディレクティブを使ったキャッシュコンポーネント(Next.js 16 の新機能)- 新しいキャッシュ API:
revalidateTag()、updateTag()、refresh()(Next.js 16 で更新) - Next.js 15 から 16 への移行(非同期 params、proxy.ts、並列ルート)
- ルートハンドラー(API エンドポイント、Webhook、ストリーミングレスポンス)
- Proxy のパターン(Next.js 16 では
proxy.tsがmiddleware.tsに置き換わります) - 非同期ルートパラメーター(
params、searchParams、cookies()、headers()が非同期になりました) - default.js を使った並列ルート(Next.js 16 の破壊的変更)
- React 19.2 の機能(View Transitions、
useEffectEvent()、React Compiler) - Metadata API(SEO、Open Graph、Twitter Cards、サイトマップ)
- 画像の最適化(Next.js 16 でデフォルト設定が更新された
next/image) - フォントの最適化(
next/fontのパターン) - Turbopack の設定(Next.js 16 で安定版となり、デフォルトで有効)
- パフォーマンスの最適化(遅延読み込み、コード分割、PPR、ISR)
- TypeScript の設定(strict モード、パスエイリアス)
このスキルを使用しない場合
次の用途には、このスキルを使用しないでください。
- Cloudflare Workers へのデプロイ → 代わりに
cloudflare-nextjsスキルを使用してください - Pages Router のパターン → このスキルの対象は App Router のみです(Pages Router はレガシーです)
- 認証ライブラリ →
clerk-auth、auth-js、または認証に特化した他のスキルを使用してください - データベース連携 →
cloudflare-d1、drizzle-orm-d1、またはデータベースに特化したスキルを使用してください - UI コンポーネントライブラリ → Tailwind と shadcn/ui には
tailwind-v4-shadcnスキルを使用してください - 状態管理 →
zustand-state-management、tanstack-queryスキルを使用してください - フォームライブラリ →
react-hook-form-zodスキルを使用してください - Vercel 固有の機能 → Vercel プラットフォームのドキュメントを参照してください
- Next.js Enterprise の機能(ISR、DPR)→ Next.js Enterprise のドキュメントを参照してください
- デプロイ設定 → プラットフォームに特化したデプロイスキルを使用してください
他のスキルとの関係:
- cloudflare-nextjs:Next.js を Cloudflare Workers にデプロイする場合に使用します(Cloudflare にデプロイする場合は、両方のスキルを併用してください)
- tailwind-v4-shadcn:Tailwind v4 と shadcn/ui のセットアップに使用します(このスキルと組み合わせて使用できます)
- clerk-auth:Next.js で Clerk 認証を使用する場合に使用します(このスキルと組み合わせて使用できます)
- auth-js:Auth.js(NextAuth)との連携に使用します(このスキルと組み合わせて使用できます)
Next.js 16 の破壊的変更
重要:Next.js 16 では、複数の破壊的変更が導入されています。Next.js 15 以前から移行する場合は、このセクションをよくお読みください。
1. 非同期ルートパラメーター(破壊的変更)
破壊的変更:params、searchParams、cookies()、headers()、draftMode() が非同期になり、await が必要です。
変更前(Next.js 15):
// ❌ This no longer works in Next.js 16
export default function Page({ params, searchParams }: {
params: { slug: string }
searchParams: { query: string }
}) {
const slug = params.slug // ❌ Error: params is a Promise
const query = searchParams.query // ❌ Error: searchParams is a Promise
return <div>{slug}</div>
}
変更後(Next.js 16):
// ✅ Correct: await params and searchParams
export default async function Page({ params, searchParams }: {
params: Promise<{ slug: string }>
searchParams: Promise<{ query: string }>
}) {
const { slug } = await params // ✅ Await the promise
const { query } = await searchParams // ✅ Await the promise
return <div>{slug}</div>
}
対象:
- ページ、レイアウト、ルートハンドラーの
params - ページの
searchParams next/headersのcookies()next/headersのheaders()next/headersのdraftMode()
移行方法:
// ❌ Before
import { cookies, headers } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies() // ❌ Sync access
const headersList = headers() // ❌ Sync access
}
// ✅ After
import { cookies, headers } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies() // ✅ Async access
const headersList = await headers() // ✅ Async access
}
Codemod:npx @next/codemod@canary upgrade latest を実行すると、自動的に移行できます。
テンプレートを参照:templates/app-router-async-params.tsx
2. Middleware から Proxy への移行(破壊的変更)
破壊的変更:Next.js 16 では middleware.ts は非推奨です。代わりに proxy.ts を使用してください。
変更の理由:proxy.ts は Node.js ランタイム(Edge ランタイムではありません)で実行されるため、ネットワーク境界が明確になります。これにより、エッジミドルウェアとサーバーサイドプロキシの違いがより明確になります。
移行手順:
- ファイル名を変更:
middleware.ts→proxy.ts - 関数名を変更:
middleware→proxy - 設定を更新:
matcher→config.matcher(構文は同じです)
変更前(Next.js 15):
// middleware.ts ❌ Deprecated in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*',
}
変更後(Next.js 16):
// proxy.ts ✅ New in Next.js 16
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
const response = NextResponse.next()
response.headers.set('x-custom-header', 'value')
return response
}
export const config = {
matcher: '/api/:path*',
}
注:Next.js 16 でも middleware.ts は引き続き動作しますが、非推奨です。今後の互換性のため、proxy.ts に移行してください。
テンプレートを参照:templates/proxy-migration.ts
リファレンスを参照:references/proxy-vs-middleware.md
3. Parallel Routes では default.js が必須(破壊的変更)
破壊的変更:Parallel Routes では、明示的な default.js ファイルが必須になりました。これがない場合、ソフトナビゲーション中にルートでエラーが発生します。
構成:
app/
├── @auth/
│ ├── login/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
├── @dashboard/
│ ├── overview/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
└── layout.tsx
レイアウト:
// app/layout.tsx
export default function Layout({
children,
auth,
dashboard,
}: {
children: React.ReactNode
auth: React.ReactNode
dashboard: React.ReactNode
}) {
return (
<html>
<body>
{auth}
{dashboard}
{children}
</body>
</html>
)
}
デフォルトのフォールバック(必須):
// app/@auth/default.tsx
export default function AuthDefault() {
return null // or <Skeleton /> or redirect
}
// app/@dashboard/default.tsx
export default function DashboardDefault() {
return null
}
必須である理由:Next.js 16 では、Parallel Routes におけるソフトナビゲーションの処理方法が変更されました。default.js がないと、クライアントサイドのナビゲーション中に一致しないスロットでエラーが発生します。
テンプレートを参照:templates/parallel-routes-with-default.tsx
4. 削除された機能(破壊的変更)
以下の機能は Next.js 16 で削除されました:
- AMP サポート - 完全に削除されました。標準ページに移行してください。
next lintコマンド - ESLint または Biome を直接使用してください。serverRuntimeConfigとpublicRuntimeConfig- 代わりに環境変数を使用してください。experimental.pprフラグ - Cache Components に発展しました。"use cache"ディレクティブを使用してください。scroll-behavior: smoothの自動設定 - 必要に応じて手動で追加してください。- Node.js 18 のサポート - 最低バージョンは 20.9+ になりました。
移行方法:
- AMP:AMP ページを標準ページに変換するか、別の AMP 実装を使用してください。
- Lint:
npx eslint .またはnpx biome lint .を直接実行してください。 - 設定:
serverRuntimeConfigをprocess.env.VARIABLEに置き換えてください。 - PPR:
experimental.pprから"use cache"ディレクティブに移行してください(Cache Components のセクションを参照)。
5. バージョン要件(破壊的変更)
Next.js 16 の要件:
- Node.js:20.9+(Node.js 18 はサポート対象外です)
- TypeScript:5.1+(TypeScript を使用する場合)
- React:19.2+(Next.js 16 とともに自動的にインストールされます)
- ブラウザー:Chrome 111+、Safari 16.4+、Firefox 109+、Edge 111+
バージョンを確認:
node --version # Should be 20.9+
npm --version # Should be 10+
npx next --version # Should be 16.0.0+
Node.js をアップグレード:
# Using nvm
nvm install 20
nvm use 20
nvm alias default 20
# Using Homebrew (macOS)
brew install node@20
# Using apt (Ubuntu/Debian)
sudo apt update
sudo apt install nodejs npm
6. 画像のデフォルト設定の変更(破壊的変更)
Next.js 16 では next/image のデフォルト設定が変更されました:
| 設定 | Next.js 15 | Next.js 16 |
|---|---|---|
| TTL(キャッシュ期間) | 60 秒 | 4 時間 |
| imageSizes | [16, 32, 48, 64, 96, 128, 256, 384] | [640, 750, 828, 1080, 1200](削減) |
| qualities | [75, 90, 100] | [75](単一画質) |
影響:
- 画像のキャッシュ期間が長くなります(60 秒から 4 時間に延長)
- 生成される画像サイズが少なくなります(ビルドサイズは小さくなりますが、サイズの刻みは粗くなります)
- 複数の画質ではなく、画質 75 のみが生成されます
デフォルト設定を上書き(必要な場合):
// next.config.ts
import type { NextConfig } from 'next'
const config: NextConfig = {
images: {
minimumCacheTTL: 60, // Revert to 60 seconds
deviceSizes: [640, 750, 828, 1080, 1200, 1920], // Add larger sizes
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], // Restore old sizes
formats: ['image/webp'], // Default
},
}
export default config
テンプレートを参照:templates/image-optimization.tsx
キャッシュコンポーネントとキャッシュ API
Next.js 16 の新機能:キャッシュコンポーネントでは、"use cache" ディレクティブによるオプトイン方式のキャッシュが導入され、Next.js 15 の暗黙的なキャッシュが置き換えられています。
1. 概要
変更点:
- Next.js 15:暗黙的なキャッシュ(デフォルトですべてのサーバーコンポーネントをキャッシュ)
- Next.js 16:
"use cache"ディレクティブによるオプトイン方式のキャッシュ
変更の理由:明示的なキャッシュにより、開発者の制御性が高まり、キャッシュの動作を予測しやすくなります。
キャッシュコンポーネントでできること:
- コンポーネント単位のキャッシュ(ページ全体ではなく、特定のコンポーネントをキャッシュ)
- 関数単位のキャッシュ(負荷の高い計算をキャッシュ)
- ページ単位のキャッシュ(ページ全体を選択的にキャッシュ)
- Partial Prerendering(PPR) - 静的な部分をキャッシュし、動的な部分をオンデマンドでレンダリング
2. "use cache" ディレクティブ
構文:サーバーコンポーネント、関数、またはルートハンドラーの先頭に "use cache" を追加します。
コンポーネント単位のキャッシュ:
// app/components/expensive-component.tsx
'use cache'
export async function ExpensiveComponent() {
const data = await fetch('https://api.example.com/data')
const json = await data.json()
return (
<div>
<h1>{json.title}</h1>
<p>{json.description}</p>
</div>
)
}
関数単位のキャッシュ:
// lib/data.ts
'use cache'
export async function getExpensiveData(id: string) {
const response = await fetch(`https://api.example.com/items/${id}`)
return response.json()
}
// Usage in component
import { getExpensiveData } from '@/lib/data'
export async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const product = await getExpensiveData(id) // Cached
return <div>{product.name}</div>
}
ページ単位のキャッシュ:
// app/blog/[slug]/page.tsx
'use cache'
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}
export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
)
}
テンプレートを参照:templates/cache-component-use-cache.tsx
3. Partial Prerendering(PPR)
PPRでは、ページの静的な部分をキャッシュしながら、動的な部分をオンデマンドでレンダリングできます。
パターン:
// app/dashboard/page.tsx
// Static header (cached)
'use cache'
async function StaticHeader() {
return <header>My App</header>
}
// Dynamic user info (not cached)
async function DynamicUserInfo() {
const cookieStore = await cookies()
const userId = cookieStore.get('userId')?.value
const user = await fetch(`/api/users/${userId}`).then(r => r.json())
return <div>Welcome, {user.name}</div>
}
// Page combines both
export default function Dashboard() {
return (
<div>
<StaticHeader /> {/* Cached */}
<DynamicUserInfo /> {/* Dynamic */}
</div>
)
}
PPR を使用する場面:
- ページに静的コンテンツと動的コンテンツの両方がある
- レイアウト、ヘッダー、フッターをキャッシュしながら、ユーザーごとのコンテンツをレンダリングしたい
- 初期読み込みの高速化(静的な部分)とパーソナライズ(動的な部分)が必要
リファレンスを参照:references/cache-components-guide.md
4. revalidateTag() - 更新された API
破壊的変更:revalidateTag() で、stale-while-revalidate 動作を有効にするには、2 番目の引数(cacheLife プロファイル)が必須になりました。
変更前(Next.js 15):
import { revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await fetch(`/api/posts/${id}`, { method: 'PATCH' })
revalidateTag('posts') // ❌ Only one argument in Next.js 15
}
変更後(Next.js 16):
import { revalidateTag } from 'next/cache'
export async function updatePost(id: string) {
await fetch(`/api/posts/${id}`, { method: 'PATCH' })
revalidateTag('posts', 'max') // ✅ Second argument required in Next.js 16
}
組み込みのキャッシュライフプロファイル:
'max'- 最大の古さ(ほとんどのユースケースで推奨)'hours'- 数時間後に古くなる'days'- 数日後に古くなる'weeks'- 数週間後に古くなる'default'- デフォルトのキャッシュ動作
カスタムのキャッシュライフプロファイル:
revalidateTag('posts', {
stale: 3600, // Stale after 1 hour (seconds)
revalidate: 86400, // Revalidate every 24 hours (seconds)
expire: false, // Never expire (optional)
})
サーバーアクションでのパターン:
'use server'
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify({ title, content }),
})
revalidateTag('posts', 'max') // ✅ Revalidate with max staleness
}
テンプレートを参照:templates/revalidate-tag-cache-life.ts
5. updateTag() - 新しい API(サーバーアクション専用)
Next.js 16 の新機能:updateTag() は、サーバーアクションで書き込み後の読み取りセマンティクスを提供します。
動作:
- キャッシュを即座に期限切れにします
- 同じリクエスト内でデータを更新します
- ミューテーション直後に更新されたデータを表示します(古いデータは表示されません)
revalidateTag() との違い:
revalidateTag():stale-while-revalidate(古いデータを表示し、バックグラウンドで再検証)updateTag():即時更新(キャッシュを期限切れにし、同じリクエスト内で最新データを取得)
ユースケース:フォーム、ユーザー設定、またはユーザーが即時のフィードバックを期待するあらゆるミューテーション。
パターン:
'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(formData: FormData) {
const name = formData.get('name') as string
const email = formData.get('email') as string
// Update database
await db.users.update({ name, email })
// Immediately refresh cache (read-your-writes)
updateTag('user-profile')
// User sees updated data immediately (no stale data)
}
使用する場面:
updateTag():ユーザー設定、プロフィールの更新、重要なミューテーション(即時のフィードバック)revalidateTag():ブログ記事、商品一覧、重要度の低い更新(バックグラウンドでの再検証)
テンプレートを参照:templates/server-action-update-tag.ts
6. refresh() - 新しい API(サーバーアクション専用)
Next.js 16 の新機能:refresh() はキャッシュされていないデータのみを更新します(クライアント側の router.refresh() を補完します)。
使用する場面:
- キャッシュされたデータに影響を与えず、動的なデータを更新する
- サーバー側で
router.refresh()を補完する
パターン:
'use server'
import { refresh } from 'next/cache'
export async function refreshDashboard() {
// Refresh uncached data (e.g., real-time metrics)
refresh()
// Cached data (e.g., static header) remains cached
}
revalidateTag() および updateTag() との違い:
refresh():キャッシュされていないデータのみを更新revalidateTag():特定のタグが付いたデータを再検証(stale-while-revalidate)updateTag():特定のタグが付いたデータを即座に期限切れにして更新
リファレンスを参照:references/cache-components-guide.md
サーバーコンポーネント
サーバーコンポーネントは、サーバー上でレンダリングされる React コンポーネントです。効率的なデータ取得を可能にし、クライアントバンドルのサイズを削減して、パフォーマンスを向上させます。
1. サーバーコンポーネントの基本
デフォルトの動作:App Router のすべてのコンポーネントは、'use client' が指定されていない限り、デフォルトでサーバーコンポーネントになります。
例:
// app/posts/page.tsx (Server Component by default)
export default async function PostsPage() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return (
<div>
{posts.map((post: { id: string; title: string }) => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
)
}
ルール:
- ✅ コンポーネント本体でプロミスを直接
awaitできます - ✅
cookies()、headers()、draftMode()にアクセスできます(awaitを使用) - ✅ Node.js API(fs、path など)を使用できます
- ❌ ブラウザー API(window、document、localStorage)は使用できません
- ❌ React フック(
useState、useEffectなど)は使用できません - ❌ イベントハンドラー(
onClick、onChangeなど)は使用できません
2. サーバーコンポーネントでのデータ取得
パターン:コンポーネント本体で async/await を直接使用します。
// app/products/[id]/page.tsx
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
// Fetch data directly in component
const product = await fetch(`https://api.example.com/products/${id}`)
.then(r => r.json())
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
</div>
)
}
並列データ取得:
export default async function Dashboard() {
// Fetch in parallel with Promise.all
const [user, posts, comments] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json()),
])
return (
<div>
<UserInfo user={user} />
<PostsList posts={posts} />
<CommentsList comments={comments} />
</div>
)
}
逐次データ取得(必要な場合):
export default async function UserPosts({ params }: { params: Promise<{ userId: string }> }) {
const { userId } = await params
// Fetch user first
const user = await fetch(`/api/users/${userId}`).then(r => r.json())
// Then fetch user's posts (depends on user data)
const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json())
return (
<div>
<h1>{user.name}'s Posts</h1>
<ul>
{posts.map((post: { id: string; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
テンプレートを参照:templates/server-component-streaming.tsx
3. Suspense によるストリーミング
パターン:時間のかかるコンポーネントを <Suspense> でラップし、読み込みに応じてコンテンツをストリーミングします。
import { Suspense } from 'react'
// Fast component (loads immediately)
async function Header() {
return <header>My App</header>
}
// Slow component (takes 2 seconds)
async function SlowData() {
await new Promise(resolve => setTimeout(resolve, 2000))
const data = await fetch('/api/slow-data').then(r => r.json())
return <div>{data.content}</div>
}
// Page streams content
export default function Page() {
return (
<div>
<Header /> {/* Loads immediately */}
<Suspense fallback={<div>Loading...</div>}>
<SlowData /> {/* Streams when ready */}
</Suspense>
</div>
)
}
ストリーミングを使用する場合:
- ページで時間のかかる API 呼び出しがある
- すべてのデータを待たずに、すぐに UI を表示したい
- 体感パフォーマンスを向上させたい
リファレンスを参照:references/server-components-patterns.md
4. サーバーコンポーネントとクライアントコンポーネント
サーバーコンポーネントを使用する場合(デフォルト):
- API/データベースからデータを取得する
- バックエンドのリソース(ファイル、環境変数)にアクセスする
- 大きな依存関係をサーバーに保持する(バンドルサイズを削減する)
- 静的コンテンツをレンダリングする
クライアントコンポーネント('use client')を使用する場合:
- React フック(
useState、useEffect、useContext)が必要 - イベントハンドラー(
onClick、onChange、onSubmit)が必要 - ブラウザー API(
window、localStorage、navigator)が必要 - ブラウザー API を使用するサードパーティライブラリ(チャート、マップなど)が必要
パターン:デフォルトではサーバーコンポーネントを使用し、必要な場合にのみ 'use client' を追加します。
// app/components/interactive-button.tsx
'use client' // Client Component
import { useState } from 'react'
export function InteractiveButton() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
)
}
// app/page.tsx (Server Component)
import { InteractiveButton } from './components/interactive-button'
export default async function Page() {
const data = await fetch('/api/data').then(r => r.json())
return (
<div>
<h1>{data.title}</h1>
<InteractiveButton /> {/* Client Component inside Server Component */}
</div>
)
}
構成ルール:
- ✅ サーバーコンポーネントはクライアントコンポーネントをインポートできます
- ✅ クライアントコンポーネントはクライアントコンポーネントをインポートできます
- ✅ クライアントコンポーネントは、props を介してサーバーコンポーネントを子要素としてレンダリングできます
- ❌ クライアントコンポーネントはサーバーコンポーネントを直接インポートできません
リファレンスを参照:references/server-components-patterns.md
サーバーアクション
サーバーアクションは、サーバー上で実行される非同期関数です。サーバー側での変更、フォーム処理、データの再検証を可能にします。
1. サーバーアクションの基本
定義:'use server' ディレクティブを追加して、サーバーアクションを作成します。
ファイルレベルのサーバーアクション:
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
// Mutate database
await db.posts.create({ title, content })
// Revalidate cache
revalidateTag('posts', 'max')
}
インラインのサーバーアクション:
// app/posts/new/page.tsx
export default function NewPostPage() {
async function createPost(formData: FormData) {
'use server'
const title = formData.get('title') as string
const content = formData.get('content') as string
await db.posts.create({ title, content })
revalidateTag('posts', 'max')
}
return (
<form action={createPost}>
<input name="title" />
<textarea name="content" />
<button type="submit">Create Post</button>
</form>
)
}
テンプレートを参照:templates/server-actions-form.tsx
2. フォーム処理
基本的なフォーム:
// app/components/create-post-form.tsx
import { createPost } from '@/app/actions'
export function CreatePostForm() {
return (
<form action={createPost}>
<label>
Title:
<input type="text" name="title" required />
</label>
<label>
Content:
<textarea name="content" required />
</label>
<button type="submit">Create Post</button>
</form>
)
}
読み込み状態を使用する場合(useFormStatus):
'use client'
import { useFormStatus } from 'react-dom'
import { createPost } from '@/app/actions'
function SubmitButton() {
const { pending } = useFormStatus()
return (
<button type="submit" disabled={pending}>
{pending ? 'Creating...' : 'Create Post'}
</button>
)
}
export function CreatePostForm() {
return (
<form action={createPost}>
<input type="text" name="title" required />
<textarea name="content" required />
<SubmitButton />
</form>
)
}
バリデーションを使用する場合:
// app/actions.ts
'use server'
import { z } from 'zod'
import { redirect } from 'next/navigation'
const PostSchema = z.object({
title: z.string().min(3, 'Title must be at least 3 characters'),
content: z.string().min(10, 'Content must be at least 10 characters'),
})
export async function createPost(formData: FormData) {
const rawData = {
title: formData.get('title'),
content: formData.get('content'),
}
// Validate
const parsed = PostSchema.safeParse(rawData)
if (!parsed.success) {
return {
errors: parsed.error.flatten().fieldErrors,
}
}
// Mutate
await db.posts.create(parsed.data)
// Revalidate and redirect
revalidateTag('posts', 'max')
redirect('/posts')
}
テンプレートを参照:templates/server-actions-form.tsx
リファレンスを参照:references/server-actions-guide.md
3. エラー処理
パターン:サーバーアクションからエラーオブジェクトを返し、クライアントコンポーネントで処理します。
サーバーアクション:
// app/actions.ts
'use server'
export async function deletePost(id: string) {
try {
await db.posts.delete({ where: { id } })
revalidateTag('posts', 'max')
return { success: true }
} catch (error) {
return {
success: false,
error: 'Failed to delete post. Please try again.'
}
}
}
クライアントコンポーネント:
'use client'
import { useState } from 'react'
import { deletePost } from '@/app/actions'
export function DeleteButton({ postId }: { postId: string }) {
const [error, setError] = useState<string | null>(null)
async function handleDelete() {
const result = await deletePost(postId)
if (!result.success) {
setError(result.error)
}
}
return (
<div>
<button onClick={handleDelete}>Delete Post</button>
{error && <p className="error">{error}</p>}
</div>
)
}
4. 楽観的更新
パターン:まず UI をすぐに更新し、その後サーバーと同期します。
'use client'
import { useOptimistic } from 'react'
import { likePost } from '@/app/actions'
export function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
const [optimisticLikes, addOptimisticLike] = useOptimistic(
initialLikes,
(state, amount: number) => state + amount
)
async function handleLike() {
// Update UI immediately
addOptimisticLike(1)
// Sync with server
await likePost(postId)
}
return (
<button onClick={handleLike}>
❤️ {optimisticLikes} likes
</button>
)
}
リファレンスを参照:references/server-actions-guide.md
ルートハンドラー
ルートハンドラーは、App Router のサーバー側 API エンドポイントです。Pages Router の API Routes に代わるものです。
1. 基本的なルートハンドラー
ファイル:app/api/hello/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
return NextResponse.json({ message: 'Hello, World!' })
}
export async function POST(request: Request) {
const body = await request.json()
return NextResponse.json({
message: 'Post created',
data: body
})
}
サポートされるメソッド:GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS
テンプレートを参照:templates/route-handler-api.ts
2. 動的ルート
ファイル:app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server'
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params // ✅ Await params in Next.js 16
const post = await db.posts.findUnique({ where: { id } })
if (!post) {
return NextResponse.json(
{ error: 'Post not found' },
{ status: 404 }
)
}
return NextResponse.json(post)
}
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params
await db.posts.delete({ where: { id } })
return NextResponse.json({ message: 'Post deleted' })
}
3. 検索パラメーター
URL:/api/posts?tag=javascript&limit=10
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const tag = searchParams.get('tag')
const limit = parseInt(searchParams.get('limit') || '10')
const posts = await db.posts.findMany({
where: { tags: { has: tag } },
take: limit,
})
return NextResponse.json(posts)
}
4. Webhook
パターン:サードパーティサービスから受信する Webhook を処理します。
// app/api/webhooks/stripe/route.ts
import { NextResponse } from 'next/server'
import { headers } from 'next/headers'
export async function POST(request: Request) {
const body = await request.text()
const headersList = await headers() // ✅ Await headers in Next.js 16
const signature = headersList.get('stripe-signature')
// Verify webhook signature
const event = stripe.webhooks.constructEvent(
body,
signature!,
process.env.STRIPE_WEBHOOK_SECRET!
)
// Handle event
switch (event.type) {
case 'payment_intent.succeeded':
await handlePaymentSuccess(event.data.object)
break
case 'payment_intent.failed':
await handlePaymentFailure(event.data.object)
break
}
return NextResponse.json({ received: true })
}
テンプレートを参照:templates/route-handler-api.ts
リファレンスを参照:references/route-handlers-reference.md
Proxy と Middleware の比較
Next.js 16 では、middleware.ts に代わる proxy.ts が導入されます。
変更の理由
middleware.ts:Edge ランタイム(Node.js API の利用に制限あり)で実行されますproxy.ts:Node.js ランタイム(すべての Node.js API を利用可能)で実行されます
新しい proxy.ts によってネットワーク境界が明確になり、柔軟性も向上します。
移行
変更前(middleware.ts):
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Check auth
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: '/dashboard/:path*',
}
変更後(proxy.ts):
// proxy.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function proxy(request: NextRequest) {
// Check auth
const token = request.cookies.get('token')
if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: '/dashboard/:path*',
}
テンプレートを参照:templates/proxy-migration.ts
リファレンスを参照:references/proxy-vs-middleware.md
並列ルートとルートグループ
1. 並列ルート
用途:同じレイアウト内に複数のページをレンダリングします(例:モーダルとメインコンテンツ、ダッシュボードのパネル)。
構成:
app/
├── @modal/
│ ├── login/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
├── @feed/
│ ├── trending/
│ │ └── page.tsx
│ └── default.tsx ← REQUIRED in Next.js 16
└── layout.tsx
レイアウト:
// app/layout.tsx
export default function Layout({
children,
modal,
feed,
}: {
children: React.ReactNode
modal: React.ReactNode
feed: React.ReactNode
}) {
return (
<html>
<body>
{modal}
<main>
{children}
<aside>{feed}</aside>
</main>
</body>
</html>
)
}
デフォルトファイル(必須):
// app/@modal/default.tsx
export default function ModalDefault() {
return null
}
// app/@feed/default.tsx
export default function FeedDefault() {
return <div>Default Feed</div>
}
テンプレートを参照:templates/parallel-routes-with-default.tsx
2. ルートグループ
用途:URL 構造に影響を与えずにルートを整理します。
構成:
app/
├── (marketing)/
│ ├── about/
│ │ └── page.tsx → /about
│ └── contact/
│ └── page.tsx → /contact
├── (shop)/
│ ├── products/
│ │ └── page.tsx → /products
│ └── cart/
│ └── page.tsx → /cart
└── layout.tsx
グループごとに異なるレイアウトを設定:
app/
├── (marketing)/
│ ├── layout.tsx ← Marketing layout
│ └── about/page.tsx
├── (shop)/
│ ├── layout.tsx ← Shop layout
│ └── products/page.tsx
└── layout.tsx ← Root layout
リファレンスを参照:references/app-router-fundamentals.md
React 19.2 の機能
Next.js 16 には React 19.2 が統合されており、React Canary の新機能が含まれています。
1. ビュートランジション
用途:ページ遷移を滑らかにアニメーションします。
'use client'
import { useRouter } from 'next/navigation'
import { startTransition } from 'react'
export function NavigationLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter()
function handleClick(e: React.MouseEvent) {
e.preventDefault()
// Wrap navigation in startTransition for View Transitions
startTransition(() => {
router.push(href)
})
}
return <a href={href} onClick={handleClick}>{children}</a>
}
CSS View Transitions API を使用:
/* app/globals.css */
@view-transition {
navigation: auto;
}
/* Animate elements with view-transition-name */
.page-title {
view-transition-name: page-title;
}
テンプレートを参照:templates/view-transitions-react-19.tsx
2. useEffectEvent()(実験的)
用途:useEffect からリアクティブではないロジックを切り離します。
'use client'
import { useEffect, experimental_useEffectEvent as useEffectEvent } from 'react'
export function ChatRoom({ roomId }: { roomId: string }) {
const onConnected = useEffectEvent(() => {
console.log('Connected to room:', roomId)
})
useEffect(() => {
const connection = connectToRoom(roomId)
onConnected() // Non-reactive callback
return () => connection.disconnect()
}, [roomId]) // Only re-run when roomId changes
return <div>Chat Room {roomId}</div>
}
使用する理由:コールバックの依存関係が変化したときに、不要な useEffect の再実行を防ぎます。
3. React Compiler(安定版)
用途:useMemo や useCallback を使わずに、自動でメモ化します。
next.config.ts で有効化:
import type { NextConfig } from 'next'
const config: NextConfig = {
experimental: {
reactCompiler: true,
},
}
export default config
プラグインをインストール:
npm install babel-plugin-react-compiler
例(手動でメモ化する必要はありません):
'use client'
export function ExpensiveList({ items }: { items: string[] }) {
// React Compiler automatically memoizes this
const filteredItems = items.filter(item => item.length > 3)
return (
<ul>
{filteredItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
)
}
リファレンスを参照:references/react-19-integration.md
Metadata API
Metadata API を使うと、型安全な SEO およびソーシャル共有用のメタデータを設定できます。
1. 静的メタデータ
パターン:ページまたはレイアウトから metadata オブジェクトをエクスポートします。
// app/page.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
description: 'Welcome to my app',
openGraph: {
title: 'My App',
description: 'Welcome to my app',
images: ['/og-image.jpg'],
},
twitter: {
card: 'summary_large_image',
title: 'My App',
description: 'Welcome to my app',
images: ['/twitter-image.jpg'],
},
}
export default function Page() {
return <h1>Home</h1>
}
2. 動的メタデータ
パターン:非同期関数 generateMetadata をエクスポートします。
// app/posts/[id]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
const post = await fetch(`/api/posts/${id}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
},
}
}
export default async function PostPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const post = await fetch(`/api/posts/${id}`).then(r => r.json())
return <article>{post.content}</article>
}
3. サイトマップ
ファイル:app/sitemap.ts
import type { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await fetch('/api/posts').then(r => r.json())
const postUrls = posts.map((post: { id: string; updatedAt: string }) => ({
url: `https://example.com/posts/${post.id}`,
lastModified: post.updatedAt,
changeFrequency: 'weekly' as const,
priority: 0.8,
}))
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
...postUrls,
]
}
テンプレートを参照:templates/metadata-config.ts
リファレンスを参照:references/metadata-api-guide.md
画像とフォントの最適化
1. next/image
基本的な使い方:
import Image from 'next/image'
export function MyImage() {
return (
<Image
src="/hero.jpg"
alt="Hero image"
width={1200}
height={600}
priority // Load above the fold
/>
)
}
レスポンシブ画像:
<Image
src="/hero.jpg"
alt="Hero"
fill
style={{ objectFit: 'cover' }}
sizes="(max-width: 768px) 100vw, 50vw"
/>
リモート画像(next.config.ts で設定):
import type { NextConfig } from 'next'
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/images/**',
},
],
},
}
export default config
テンプレートを参照:templates/image-optimization.tsx
2. next/font
Google Fonts:
// app/layout.tsx
import { Inter, Roboto_Mono } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
const robotoMono = Roboto_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-roboto-mono',
})
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
<body>{children}</body>
</html>
)
}
ローカルフォント:
import localFont from 'next/font/local'
const myFont = localFont({
src: './fonts/my-font.woff2',
display: 'swap',
variable: '--font-my-font',
})
テンプレートを参照:templates/font-optimization.tsx
パフォーマンスのパターン
1. 遅延読み込み
コンポーネントの遅延読み込み:
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./heavy-component'), {
loading: () => <div>Loading...</div>,
ssr: false, // Disable SSR for client-only components
})
export default function Page() {
return (
<div>
<h1>My Page</h1>
<HeavyComponent />
</div>
)
}
条件付き読み込み:
'use client'
import { useState } from 'react'
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('./chart'), { ssr: false })
export function Dashboard() {
const [showChart, setShowChart] = useState(false)
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && <Chart />}
</div>
)
}
2. コード分割
ルート単位の分割(自動):
app/
├── page.tsx → Bundles: /, shared
├── about/
│ └── page.tsx → Bundles: /about, shared
└── products/
└── page.tsx → Bundles: /products, shared
コンポーネント単位の分割(動的インポートを使用):
const Analytics = dynamic(() => import('./analytics'))
const Comments = dynamic(() => import('./comments'))
export default function BlogPost() {
return (
<article>
<h1>Post Title</h1>
<p>Content...</p>
<Analytics /> {/* Separate bundle */}
<Comments /> {/* Separate bundle */}
</article>
)
}
3. Turbopack(Next.js 16 で安定版)
デフォルト:Next.js 16 では Turbopack がデフォルトのバンドラーになりました。
指標:
- 本番ビルドが 2~5 倍高速
- Fast Refresh が最大 10 倍高速
無効化(必要な場合):
npm run build -- --webpack
ファイルシステムキャッシュを有効化(ベータ版):
// next.config.ts
import type { NextConfig } from 'next'
const config: NextConfig = {
experimental: {
turbopack: {
fileSystemCaching: true, // Beta: Persist cache between runs
},
},
}
export default config
リファレンスを参照:references/performance-optimization.md
TypeScript の設定
1. 厳格モード
tsconfig.json で厳格モードを有効化:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
2. パスエイリアス
tsconfig.json で設定:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./app/*"],
"@/components/*": ["./app/components/*"],
"@/lib/*": ["./lib/*"],
"@/styles/*": ["./styles/*"]
}
}
}
使い方:
// Instead of: import { Button } from '../../../components/button'
import { Button } from '@/components/button'
3. 型安全なルーティング
ルートから型を生成:
npm run build
使い方:
import { useRouter } from 'next/navigation'
const router = useRouter()
// Type-safe routing
router.push('/posts/123') // ✅ Valid route
router.push('/invalid') // ❌ Type error if route doesn't exist
リファレンスを参照:references/typescript-configuration.md
よくあるエラーと解決方法
1. エラー:params は Promise です
エラー:
Type 'Promise<{ id: string }>' is not assignable to type '{ id: string }'
原因:Next.js 16 では params が非同期に変更されました。
解決方法:params を await します:
// ❌ Before
export default function Page({ params }: { params: { id: string } }) {
const id = params.id
}
// ✅ After
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
}
2. エラー:searchParams は Promise です
エラー:
Property 'query' does not exist on type 'Promise<{ query: string }>'
原因:Next.js 16 では searchParams が非同期になりました。
解決方法:
// ❌ Before
export default function Page({ searchParams }: { searchParams: { query: string } }) {
const query = searchParams.query
}
// ✅ After
export default async function Page({ searchParams }: { searchParams: Promise<{ query: string }> }) {
const { query } = await searchParams
}
3. エラー:cookies() に await が必要です
エラー:
'cookies' implicitly has return type 'any'
原因:Next.js 16 では cookies() が非同期になりました。
解決策:
// ❌ Before
import { cookies } from 'next/headers'
export function MyComponent() {
const cookieStore = cookies()
}
// ✅ After
import { cookies } from 'next/headers'
export async function MyComponent() {
const cookieStore = await cookies()
}
4. エラー:並列ルートに default.js がありません
エラー:
Error: Parallel route @modal/login was matched but no default.js was found
原因:Next.js 16 では、すべての並列ルートに default.js が必要です。
解決策:default.tsx ファイルを追加します。
// app/@modal/default.tsx
export default function ModalDefault() {
return null
}
5. エラー:revalidateTag() には 2 つの引数が必要です
エラー:
Expected 2 arguments, but got 1
原因:Next.js 16 では、revalidateTag() に cacheLife 引数が必要になりました。
解決策:
// ❌ Before
revalidateTag('posts')
// ✅ After
revalidateTag('posts', 'max')
6. エラー:Server Component で React フックを使用できません
エラー:
You're importing a component that needs useState. It only works in a Client Component
原因:Server Component で React フックを使用しています。
解決策:'use client' ディレクティブを追加します。
// ✅ Add 'use client' at the top
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
7. エラー:middleware.ts は非推奨です
警告:
Warning: middleware.ts is deprecated. Use proxy.ts instead.
解決策:proxy.ts に移行します。
// Rename: middleware.ts → proxy.ts
// Rename function: middleware → proxy
export function proxy(request: NextRequest) {
// Same logic
}
8. エラー:Turbopack のビルドに失敗しました
エラー:
Error: Failed to compile with Turbopack
原因:Next.js 16 では Turbopack がデフォルトになりました。
解決策:互換性がない場合は Turbopack を無効にします。
npm run build -- --webpack
9. エラー:next/image の src が無効です
エラー:
Invalid src prop (https://example.com/image.jpg) on `next/image`. Hostname "example.com" is not configured under images in your `next.config.js`
解決策:next.config.ts にリモートパターンを追加します。
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'example.com',
},
],
},
}
10. エラー:Server Component を Client Component にインポートできません
エラー:
You're importing a Server Component into a Client Component
解決策:Server Component を子要素として渡します。
// ❌ Wrong
'use client'
import { ServerComponent } from './server-component' // Error
export function ClientComponent() {
return <ServerComponent />
}
// ✅ Correct
'use client'
export function ClientComponent({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
// Usage
<ClientComponent>
<ServerComponent /> {/* Pass as children */}
</ClientComponent>
11. エラー:generateStaticParams が機能しません
原因:generateStaticParams は静的生成(export const dynamic = 'force-static')でのみ機能します。
解決策:
export const dynamic = 'force-static'
export async function generateStaticParams() {
const posts = await fetch('/api/posts').then(r => r.json())
return posts.map((post: { id: string }) => ({ id: post.id }))
}
12. エラー:fetch() がキャッシュされません
原因:Next.js 16 では "use cache" ディレクティブによるオプトイン方式のキャッシュを使用します。
解決策:コンポーネントまたは関数に "use cache" を追加します。
'use cache'
export async function getPosts() {
const response = await fetch('/api/posts')
return response.json()
}
13. エラー:Route Groups でルートが衝突しています
エラー:
Error: Conflicting routes: /about and /(marketing)/about
原因:ルートグループによって同じ URL パスが作成されています。
解決策:ルートグループが競合しないようにします。
app/
├── (marketing)/about/page.tsx → /about
└── (shop)/about/page.tsx → ERROR: Duplicate /about
# Fix: Use different routes
app/
├── (marketing)/about/page.tsx → /about
└── (shop)/store-info/page.tsx → /store-info
14. エラー:メタデータが更新されません
原因:generateMetadata() を使用せずに動的なメタデータを使用しています。
解決策:動的ページでは generateMetadata() を使用します。
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params
const post = await fetch(`/api/posts/${id}`).then(r => r.json())
return {
title: post.title,
description: post.excerpt,
}
}
15. エラー:next/font のフォントが読み込まれません
原因:フォント変数が HTML 要素に適用されていません。
解決策:フォント変数を <html> または <body> に適用します。
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html className={inter.variable}> {/* ✅ Apply variable */}
<body>{children}</body>
</html>
)
}
16. エラー:環境変数をブラウザーで使用できません
原因:サーバー専用の環境変数はブラウザーに公開されません。
解決策:クライアント側からアクセスするには、NEXT_PUBLIC_ をプレフィックスとして付けます。
# .env
SECRET_KEY=abc123 # Server-only
NEXT_PUBLIC_API_URL=https://api # Available in browser
// Server Component (both work)
const secret = process.env.SECRET_KEY
const apiUrl = process.env.NEXT_PUBLIC_API_URL
// Client Component (only public vars work)
const apiUrl = process.env.NEXT_PUBLIC_API_URL
17. エラー:Server Action が見つかりません
エラー:
Error: Could not find Server Action
原因:'use server' ディレクティブがありません。
解決策:'use server' を追加します。
// ❌ Before
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
// ✅ After
'use server'
export async function createPost(formData: FormData) {
await db.posts.create({ ... })
}
18. エラー:TypeScript のパスエイリアスが機能しません
原因:tsconfig.json の baseUrl または paths が正しくありません。
解決策:正しく設定します。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@/components/*": ["./app/components/*"]
}
}
}
参考資料:references/top-errors.md
テンプレートリファレンス
templates/ で利用できるテンプレートは次のとおりです。
App Router の基礎:
app-router-async-params.tsx- 非同期 params、searchParams のパターン(Next.js 16)parallel-routes-with-default.tsx- 必須の default.js を使用した並列ルートroute-groups-example.tsx- ルートグループによる整理
Cache Components(Next.js 16):
cache-component-use-cache.tsx-"use cache"を使用した Cache Componentspartial-prerendering.tsx- 静的部分と動的部分を組み合わせた PPRrevalidate-tag-cache-life.ts- 更新されたrevalidateTag()APIserver-action-update-tag.ts- 読み取り後の書き込みを反映するupdateTag()
Server Components:
server-component-data-fetching.tsx- データ取得のパターンserver-component-streaming.tsx- Suspense を使用したストリーミングserver-component-composition.tsx- Server Component と Client Component の構成
Server Actions:
server-actions-form.tsx- Server Actions によるフォーム処理server-actions-validation.ts- Zod による Server Action の検証server-actions-optimistic.tsx- 楽観的更新
Route Handlers:
route-handler-api.ts- 基本的な CRUD APIroute-handler-webhook.ts- Webhook の処理route-handler-streaming.ts- ストリーミングレスポンス
Proxy と Middleware:
proxy-migration.ts- middleware.ts から proxy.ts への移行proxy-auth.ts- proxy.ts による認証
React 19.2:
view-transitions-react-19.tsx- View Transitions APIuse-effect-event.tsx-useEffectEvent()のパターンreact-compiler-example.tsx- React Compiler の使用方法
メタデータ:
metadata-config.ts- 静的および動的メタデータsitemap.ts- サイトマップの生成robots.ts- robots.txt の生成
最適化:
image-optimization.tsx- next/image のパターンfont-optimization.tsx- next/font のパターンlazy-loading.tsx- 動的インポートと遅延読み込みcode-splitting.tsx- コード分割の戦略
TypeScript:
typescript-config.json- 推奨される TypeScript 設定path-aliases.tsx- パスエイリアスの使用方法
設定:
next.config.ts- Next.js の完全な設定package.json- Next.js 16 用の推奨依存関係
追加リソース
同梱のリファレンス(references/ 内):
next-16-migration-guide.md- Next.js 15 から 16 への完全な移行ガイドcache-components-guide.md- Cache Components の詳細解説proxy-vs-middleware.md- Proxy.ts と middleware.ts の比較async-route-params.md- 非同期 params、searchParams、cookies()、headers()app-router-fundamentals.md- App Router の概念とパターンserver-components-patterns.md- Server Components のベストプラクティスserver-actions-guide.md- Server Actions のパターンと検証route-handlers-reference.md- Route Handlers API リファレンスmetadata-api-guide.md- Metadata API の完全ガイドperformance-optimization.md- パフォーマンスのパターンと Turbopackreact-19-integration.md- Next.js における React 19.2 の機能top-errors.md- 18 件以上のよくあるエラーと解決策
スクリプト:
scripts/check-versions.sh- Next.js と依存関係のバージョンを確認
外部ドキュメント:
- Next.js 16 ブログ: https://nextjs.org/blog/next-16
- Next.js ドキュメント: https://nextjs.org/docs
- React ドキュメント: https://react.dev
- Context7 MCP: 最新の Next.js リファレンスには
/websites/nextjsを使用
バージョン互換性
| パッケージ | 最小バージョン | 推奨 |
|---|---|---|
| Next.js | 16.0.0 | 16.0.0+ |
| React | 19.2.0 | 19.2.0+ |
| Node.js | 20.9.0 | 20.9.0+ |
| TypeScript | 5.1.0 | 5.7.0+ |
| Turbopack | (組み込み) | 安定版 |
バージョンの確認:
./scripts/check-versions.sh
トークン効率
推定トークン削減率:65~70%
スキルなし(ドキュメントを参照して手動でセットアップ):
- Next.js 16 の移行ガイドを読む:約5kトークン
- App Router のドキュメントを読む:約8kトークン
- Server Actions のドキュメントを読む:約4kトークン
- Metadata API のドキュメントを読む:約3kトークン
- 試行錯誤による修正:約8kトークン
- 合計:約28kトークン
スキルあり:
- スキルを読み込む:約8kトークン
- テンプレートを使用する:約2kトークン
- 合計:約10kトークン
- 削減量:約18kトークン(約64%)
防止できるエラー:よくあるミス18件以上を防止=エラーを100%防止
メンテナンス
最終確認日:2025-10-24 次回レビュー:2026-01-24(四半期ごと) メンテナー:Jezweb | [email protected] リポジトリ:https://github.com/jezweb/claude-skills
更新の契機:
- Next.js のメジャー/マイナーリリース
- React のメジャーリリース
- API の破壊的変更
- Turbopack の新機能
バージョン確認:
cd skills/nextjs
./scripts/check-versions.sh
SKILL.md の終わり