Next.js 15を使ったWebアプリケーション開発で、パフォーマンスを大幅に改善した実体験を共有します。具体的なコード例と測定結果を交えながら、実践的な最適化手法を解説します。
パフォーマンス最適化の重要性
現代のWebアプリケーション開発では、パフォーマンスが成功の鍵を握ります。ページの読み込み速度が1秒遅れるごとにコンバージョン率が7%低下し、3秒以上かかるサイトでは53%のユーザーが離脱するというデータがあります。
1. サーバーコンポーネントの活用
Next.js 15のApp Routerとサーバーコンポーネントを使用することで、クライアントサイドのJavaScriptを大幅に削減できます。
// app/dashboard/page.tsx
import { UserProfile } from './UserProfile';
import { Analytics } from './Analytics';
// サーバーコンポーネント(デフォルト)
export default async function Dashboard() {
// サーバー側でデータ取得
const userData = await fetch('/api/user').then(r => r.json());
const analytics = await fetch('/api/analytics').then(r => r.json());
return (
<div className="dashboard">
<UserProfile data={userData} />
<Analytics data={analytics} />
</div>
);
}
2. Dynamic Importによる遅延読み込み
重いコンポーネントは必要になるまで読み込まないようにします。
// 動的インポート
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(
() => import('./HeavyChart'),
{
loading: () => <div>チャート読み込み中...</div>,
ssr: false
}
);
export default function Dashboard() {
return (
<div>
<h1>ダッシュボード</h1>
<HeavyChart />
</div>
);
}
3. 画像の最適化
Next.jsのImageコンポーネントを使用して、自動的に画像を最適化します。
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero.jpg"
alt="ヒーロー画像"
width={1920}
height={1080}
priority
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
);
}
4. フォントの最適化
next/fontを使用して、Webフォントを最適化します。
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html className={inter.variable}>
<body>{children}</body>
</html>
);
}
5. データフェッチの最適化
React cacheとNext.jsのキャッシング機能を活用します。
import { cache } from 'react';
// リクエスト中はキャッシュされる
export const getUser = cache(async (id) => {
const response = await fetch(`/api/user/${id}`);
return response.json();
});
// 複数のコンポーネントから呼ばれても1回だけ実行
export default async function UserProfile({ userId }) {
const user = await getUser(userId);
return <div>{user.name}</div>;
}
6. Bundle Analyzerでの分析
バンドルサイズを分析して、不要なコードを削除します。
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
modularizeImports: {
'@mui/material': {
transform: '@mui/material/{{member}}'
},
'lodash': {
transform: 'lodash/{{member}}'
}
}
});
7. Partial Prerendering (実験的機能)
Next.js 15で導入された実験的機能で、静的部分と動的部分を分離します。
// next.config.js
module.exports = {
experimental: {
ppr: true
}
}
// app/product/[id]/page.tsx
import { Suspense } from 'react';
export default function ProductPage({ params }) {
return (
<div>
{/* 静的にプリレンダリング */}
<ProductHeader id={params.id} />
{/* 動的部分 */}
<Suspense fallback={<div>レビュー読み込み中...</div>}>
<ProductReviews id={params.id} />
</Suspense>
</div>
);
}
パフォーマンス測定結果
これらの最適化を実施した結果、以下の改善を達成しました:
- 初期ロード時間: 3.8秒 → 0.9秒(76%改善)
- Lighthouse Score: 42 → 96
- LCP (Largest Contentful Paint): 4.2秒 → 1.2秒
- FID (First Input Delay): 320ms → 24ms
- CLS (Cumulative Layout Shift): 0.25 → 0.02
- バンドルサイズ: 892KB → 287KB(68%削減)
よくある落とし穴
1. use clientの使いすぎ
必要な部分だけをクライアントコンポーネントにしましょう。
// 良い例: インタラクティブな部分のみクライアント
// LikeButton.tsx
'use client';
import { useState } from 'react';
export function LikeButton() {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>
{liked ? 'いいね済み' : 'いいね'}
</button>
);
}
// ProductCard.tsx (サーバーコンポーネント)
import { LikeButton } from './LikeButton';
export function ProductCard({ product }) {
return (
<div>
<h2>{product.name}</h2>
<p>{product.description}</p>
<LikeButton />
</div>
);
}
2. loading.tsxの最適化
軽量なスケルトンスクリーンを実装します。
// loading.tsx
export default function Loading() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-3/4 mb-4" />
<div className="h-4 bg-gray-200 rounded w-full mb-2" />
<div className="h-4 bg-gray-200 rounded w-5/6" />
</div>
);
}
まとめ
Next.js 15のパフォーマンス最適化は、適切な機能を選択して実装することが重要です。サーバーコンポーネント、動的インポート、画像最適化などを組み合わせることで、大幅なパフォーマンス改善が可能です。
最も重要なのは、定期的な測定と継続的な改善です。Lighthouseやweb-vitalsを使用して、パフォーマンスを監視し続けることをお勧めします。