mcpField
N
nextjs

nextjs

github.com/jackspace開発1659 回インストール13 週間前に更新
リポジトリ

nextjs について

nextjs は Next.js 16 の App Router を使う開発者向けのスキルです。ルーティング、キャッシュ、サーバーコンポーネント、移行時の変更、React 19.2、パフォーマンス、一般的なエラーを扱います。

主な機能

  1. 01App Router のルーティングとレイアウトパターン
  2. 02Server Components と Server Actions
  3. 03Cache Components とキャッシュ API
  4. 04Next.js 15 から 16 への移行ガイド
  5. 05メタデータ・画像・フォントの最適化
  6. 06一般的なエラーとテンプレートの参照

活用シーン

  1. 01Next.js 16 App Router でページを構築
  2. 02サーバー側データ取得とフォーム更新の実装
  3. 03キャッシュと部分プリレンダリングの設定
  4. 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


目次

  1. このスキルを使用する場合
  2. このスキルを使用しない場合
  3. Next.js 16 の破壊的変更
  4. キャッシュコンポーネントとキャッシュ API
  5. サーバーコンポーネント
  6. サーバーアクション
  7. ルートハンドラー
  8. Proxy と Middleware の比較
  9. 並列ルートとルートグループ
  10. React 19.2 の機能
  11. Metadata API
  12. 画像とフォントの最適化
  13. パフォーマンスパターン
  14. TypeScript の設定
  15. よくあるエラーと解決策
  16. テンプレートリファレンス
  17. 追加リソース

このスキルを使用する場合

次のようなニーズがある場合に、このスキルを使用してください。

  • 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 ランタイムではありません)で実行されるため、ネットワーク境界が明確になります。これにより、エッジミドルウェアとサーバーサイドプロキシの違いがより明確になります。

移行手順:

  1. ファイル名を変更:middleware.ts → proxy.ts
  2. 関数名を変更:middleware → proxy
  3. 設定を更新: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 で削除されました:

  1. AMP サポート - 完全に削除されました。標準ページに移行してください。
  2. next lint コマンド - ESLint または Biome を直接使用してください。
  3. serverRuntimeConfig と publicRuntimeConfig - 代わりに環境変数を使用してください。
  4. experimental.ppr フラグ - Cache Components に発展しました。"use cache" ディレクティブを使用してください。
  5. scroll-behavior: smooth の自動設定 - 必要に応じて手動で追加してください。
  6. 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 15Next.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 Components
  • partial-prerendering.tsx - 静的部分と動的部分を組み合わせた PPR
  • revalidate-tag-cache-life.ts - 更新された revalidateTag() API
  • server-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 API
  • route-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 API
  • use-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 - パフォーマンスのパターンと Turbopack
  • react-19-integration.md - Next.js における React 19.2 の機能
  • top-errors.md - 18 件以上のよくあるエラーと解決策

スクリプト:

  • scripts/check-versions.sh - Next.js と依存関係のバージョンを確認

外部ドキュメント:


バージョン互換性

パッケージ最小バージョン推奨
Next.js16.0.016.0.0+
React19.2.019.2.0+
Node.js20.9.020.9.0+
TypeScript5.1.05.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 の終わり

コメント