nextjs-performance について
Next.js アプリケーションの開発者向けパフォーマンス最適化ガイドです。Core Web Vitals、コンポーネント設計、キャッシュ、バンドル削減に加え、Next.js 16 と React 19 のパターンも扱います。
主な機能
- 01Core Web Vitals の最適化
- 02画像とフォントの最適化
- 03キャッシュと再検証
- 04Server Component のパターン
- 05Suspense によるストリーミング
- 06バンドルの分析と削減
活用シーン
- 01LCP・INP・CLS の改善
- 02クライアント側 JavaScript の削減
- 03繰り返し行うデータ読み込みの高速化
- 04初期バンドルサイズの削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill nextjs-performanceインストール後、Claude Code で/nextjs-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-routes.mdbundle-optimization.mdcaching-strategies.mdcore-web-vitals.mdfont-optimization.mdimage-optimization.mdmetadata-seo.mdnextjs-16-patterns.mdserver-components.mdstreaming-suspense.md詳細
Next.js のパフォーマンス最適化
Core Web Vitals、最新のパターン、ベストプラクティスに重点を置いた、Next.js アプリケーション最適化の専門的なガイダンスです。
概要
このスキルでは、Next.js アプリケーションを最適化するための包括的なガイダンスを提供します。Core Web Vitals(LCP、INP、CLS)の最適化、最新の React パターン、Server Components、キャッシュ戦略、バンドル最適化の手法を扱います。React/Next.js に精通し、本番環境に適した最適化を実装したい開発者向けに設計されています。
使用する場面
Next.js アプリケーションで作業しており、次のことが必要な場合にこのスキルを使用してください。
- Core Web Vitals(LCP、INP、CLS)を最適化して、パフォーマンスと SEO を向上させる
next/imageで画像を最適化し、読み込みを高速化するnext/fontでフォントを最適化し、レイアウトシフトをなくすunstable_cache、revalidateTag、または ISR を使ったキャッシュ戦略を設定する- Client Components を Server Components に変換して、バンドルサイズを削減する
- Suspense ストリーミングを実装し、ページを段階的に読み込む
- コード分割と動的インポートでバンドルサイズを分析・削減する
- メタデータと SEO を設定し、検索エンジンでの可視性を高める
- パフォーマンス向上のために API ルートハンドラーを最適化する
- Next.js 16 と React 19 の最新パターンを適用する
対象分野
- Core Web Vitals の最適化(LCP、INP、CLS)
next/imageによる画像の最適化next/fontによるフォントの最適化- キャッシュ戦略(
unstable_cache、revalidateTag、ISR) - Server Components のパターンと Client から Server への変換
- 段階的な読み込みのためのストリーミングと Suspense
- バンドルの最適化とコード分割
- メタデータと SEO の設定
- ルートハンドラーの最適化
- Next.js 16 + React 19 のパターン
手順
開始前
- Lighthouse で現在のパフォーマンスを分析します
- ボトルネックを特定します。Chrome DevTools または PageSpeed Insights で Core Web Vitals を確認してください
- 最適化の優先順位を決定します。
- LCP の問題 → 画像とフォントに注目します
- INP の問題 → JS を減らし、Server Components を使用します
- CLS の問題 → 寸法を指定し、next/font を使用します
このスキルの使い方
-
最適化する領域に応じて、関連するリファレンスファイルを読み込みます。
- 画像の問題 →
references/image-optimization.md - フォント/レイアウトシフト →
references/font-optimization.md - キャッシュ →
references/caching-strategies.md - コンポーネントアーキテクチャ →
references/server-components.md
- 画像の問題 →
-
よくある最適化では、クイックパターンに従います
-
既存のコードを改善するために、変更前/変更後の変換を適用します
-
変更後に Lighthouse で改善を検証します
基本原則
- Server Components を優先する - 必要な場合(ブラウザー API、インタラクティブ機能)に限り 'use client' を使用します
- コンポーネントは可能な限り下層で読み込む - Client Components はリーフノードに配置します
- Suspense 境界を使用する - ストリーミングと段階的な読み込みを有効にします
- 適切にキャッシュする - 細かな再検証にはタグを使用します
- 変更前後を測定する - 実際のメトリクスで改善を必ず検証します
例
例 1: Client Component を Server Component に変換する
変更前(useEffect を使った Client Component):
'use client'
import { useEffect, useState } from 'react'
export default function ProductList() {
const [products, setProducts] = useState([])
useEffect(() => {
fetch('/api/products').then(r => r.json()).then(setProducts)
}, [])
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
変更後(データに直接アクセスする Server Component):
import { db } from '@/lib/db'
export default async function ProductList() {
const products = await db.product.findMany()
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
}
例 2: LCP 向けに画像を最適化する
import Image from 'next/image'
export function Hero() {
return (
<div className="relative w-full h-[600px]">
<Image
src="/hero.jpg"
alt="Hero"
fill
priority // Disable lazy loading for LCP
sizes="100vw"
className="object-cover"
/>
</div>
)
}
例 3: キャッシュ戦略を実装する
import { unstable_cache, revalidateTag } from 'next/cache'
// Cached data function
const getProducts = unstable_cache(
async () => db.product.findMany(),
['products'],
{ revalidate: 3600, tags: ['products'] }
)
// Revalidate on mutation
export async function createProduct(data: FormData) {
'use server'
await db.product.create({ data })
revalidateTag('products')
}
例 4: 最適化されたフォントを設定する
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body className={`${inter.className} antialiased`}>
{children}
</body>
</html>
)
}
例 5: Suspense ストリーミングを実装する
import { Suspense } from 'react'
export default function Page() {
return (
<>
<header>Static content (immediate)</header>
<Suspense fallback={<ProductSkeleton />}>
<ProductList /> {/* Streamed when ready */}
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews /> {/* Independent streaming */}
</Suspense>
</>
)
}
リファレンスドキュメント
特定の領域で作業する際は、以下のリファレンスを読み込んでください。
| トピック | リファレンスファイル |
|---|---|
| Core Web Vitals | references/core-web-vitals.md |
| 画像の最適化 | references/image-optimization.md |
| フォントの最適化 | references/font-optimization.md |
| キャッシュ戦略 | references/caching-strategies.md |
| サーバーコンポーネント | references/server-components.md |
| ストリーミング/Suspense | references/streaming-suspense.md |
| バンドルの最適化 | references/bundle-optimization.md |
| メタデータ/SEO | references/metadata-seo.md |
| API ルート | references/api-routes.md |
| Next.js 16 のパターン | references/nextjs-16-patterns.md |
よくある変換
| 変換元 | 変換先 | 効果 |
|---|---|---|
useEffect + fetch | Server Component 内で直接 async を実行 | JS を 70% 削減、TTFB を高速化 |
データ用の useState | DB に直接アクセスする Server Component | コードを簡素化し、ハイドレーションが不要 |
| クライアントサイドの fetch | unstable_cache または ISR | 繰り返しの読み込みを高速化 |
img タグ | next/image | 最適化された形式、遅延読み込み |
| CSS のフォントインポート | next/font | CLS をゼロにし、自動で最適化 |
| 重いコンポーネントの静的インポート | dynamic() | 初期バンドルを縮小 |
ベストプラクティス
画像
- すべての画像に
next/imageを使用します - LCP 画像にのみ
priorityを追加します widthとheight、またはsizesを指定したfillを設定します- UX を向上させるために
placeholder="blur"を使用します - next.config.js で remotePatterns を設定します
フォント
- CSS インポートではなく
next/fontを使用します - サイズを抑えるために
subsetsを指定します - テキストをすぐに表示するために
display: 'swap'を使用します variableオプションで CSS 変数を作成します- CSS 変数を使用するように Tailwind を設定します
キャッシュ
- コストの高いクエリを
unstable_cacheでキャッシュします - 細かな制御ができるよう、意味のあるキャッシュタグを使用します
- 動的コンテンツにはオンデマンド再検証を実装します
- データの変更頻度に応じて TTL を設定します
- ルート単位の無効化には revalidatePath を使用します
コンポーネント
- 可能な場合は Client Component を Server Component に変換します
- Client Component は末端の階層に配置します
- 段階的な読み込みには Suspense 境界を使用します
- 適切な読み込み状態を実装します
- フォールドより下にある重いコンポーネントには dynamic() を使用します
バンドル
- 重いコンポーネントを
dynamic()で遅延読み込みします - ツリーシェイキングを改善するために名前付きエクスポートを使用します
@next/bundle-analyzer で定期的にバンドルを分析します- CommonJS より ESM パッケージを優先します
- 大規模なライブラリには modularizeImports を使用します
制約と警告
Server Component の制限
- ブラウザー API(window、localStorage、document)は使用できません
- React フック(useState、useEffect、useContext)は使用できません
- イベントハンドラー(onClick、onSubmit)は使用できません
- ssr: false を指定した動的インポートは使用できません
画像最適化の制約
priorityはフォールドより上にある画像にのみ使用してください- 外部画像には next.config.js での設定が必要です
fillを使用しない限り、widthとheightが必須です- アニメーション GIF はデフォルトでは最適化されません
キャッシュに関する考慮事項
- キャッシュタグは手動で無効化する必要があります
- 開発環境ではデータキャッシュはリクエストごとです
- Edge ランタイムではキャッシュの動作が異なります
- ユーザー固有のデータをキャッシュする場合は注意してください
バンドルサイズに関する警告
- 重要なコンテンツに動的インポートを使用すると、SEO に影響する可能性があります
- ツリーシェイキングには適切な ES モジュールの使用が必要です
- ツリーシェイキングできないライブラリもあります(バレルエクスポートは避けてください)
- Client Component はバンドルサイズを増加させるため、使用は最小限にしてください
Next.js 16 と React 19 の固有事項
非同期 Params
// Next.js 15+ params is a Promise
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await fetchPost(slug)
return <article>{post.content}</article>
}
Promise に対する use() フック
'use client'
import { use, Suspense } from 'react'
function Comments({ promise }: { promise: Promise<Comment[]> }) {
const comments = use(promise) // Suspend until resolved
return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>
}
UI の更新に対する useOptimistic
'use client'
import { useOptimistic } from 'react'
export function TodoList({ todos }: { todos: Todo[] }) {
const [optimisticTodos, addOptimisticTodo] = useOptimistic(
todos,
(state, newTodo: Todo) => [...state, newTodo]
)
async function addTodo(formData: FormData) {
const text = formData.get('text') as string
addOptimisticTodo({ id: crypto.randomUUID(), text, completed: false })
await createTodo(text)
}
return (
<form action={addTodo}>
<input name="text" />
{optimisticTodos.map(todo => <div key={todo.id}>{todo.text}</div>)}
</form>
)
}
バンドル分析
# Install analyzer
npm install --save-dev @next/bundle-analyzer
# Run analysis
ANALYZE=true npm run build
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
})
module.exports = withBundleAnalyzer({
modularizeImports: {
'lodash': { transform: 'lodash/{{member}}' },
},
})
パフォーマンスチェックリスト
- すべての画像で、適切な寸法を指定した
next/imageを使用する - LCP 画像に
priority属性を設定する - フォントにはサブセットを指定した
next/fontを使用する - 可能な限りサーバーコンポーネントを使用する
- クライアントコンポーネントはリーフレベルでのみ使用する
- データ取得には Suspense 境界を設ける
- 負荷の高い処理にキャッシュを設定する
- バンドルを解析して重複を確認する
- 重量級のコンポーネントを遅延読み込みする
- Lighthouse のスコアを変更前後で確認する
よくある間違い
// ❌ DON'T: Fetch in useEffect
'use client'
useEffect(() => { fetch('/api/data').then(...) }, [])
// ✅ DO: Fetch directly in Server Component
const data = await fetch('/api/data')
// ❌ DON'T: Forget dimensions on images
<Image src="/photo.jpg" />
// ✅ DO: Always provide dimensions
<Image src="/photo.jpg" width={800} height={600} />
// ❌ DON'T: Use priority on all images
<Image src="/photo1.jpg" priority />
<Image src="/photo2.jpg" priority />
// ✅ DO: Priority only for LCP
<Image src="/hero.jpg" priority />
<Image src="/photo.jpg" loading="lazy" />
// ❌ DON'T: Cache everything with same TTL
{ revalidate: 3600 }
// ✅ DO: Match TTL to data change frequency
{ revalidate: 86400 } // Categories rarely change
{ revalidate: 60 } // Comments change often