はじめに
Next.js の App Router が安定版になってから1年以上が経ち、ベストプラクティスも固まってきました。本記事では、実際のプロダクション運用で得た知見をまとめます。
Server Components と Client Components の使い分け
Server Components を使うべきケース
- データベースへの直接アクセスが必要な場合
- 機密情報(APIキーなど)を扱う場合
- 大きな依存関係を使うが、クライアントバンドルに含めたくない場合
Client Components を使うべきケース
useState,useEffectなどの React Hook を使う場合- ブラウザ API にアクセスする場合
- イベントハンドラが必要な場合
// Server Component(デフォルト)
async function UserProfile({ userId }: { userId: string }) {
const user = await db.user.findUnique({ where: { id: userId } });
return <div>{user.name}</div>;
}
// Client Component
("use client");
function LikeButton() {
const [liked, setLiked] = useState(false);
return <button onClick={() => setLiked(!liked)}>♥</button>;
}
データフェッチ戦略
App Router では、コンポーネント単位でデータをフェッチするのが基本方針です。
リクエストの重複排除
React は同一リクエストを自動的に重複排除します。同じデータを複数のコンポーネントで必要とする場合、それぞれのコンポーネントで fetch を呼んでも問題ありません。
Streaming と Suspense
loading.tsx や <Suspense> を使うことで、ページの一部を先に表示し、残りを後からストリーミングできます。
import { Suspense } from "react";
export default function Page() {
return (
<div>
<h1>ダッシュボード</h1>
<Suspense fallback={<Skeleton />}>
<SlowComponent />
</Suspense>
</div>
);
}
キャッシュ制御
Next.js 15 以降、fetch のデフォルトキャッシュ動作が変更されました。明示的にキャッシュ戦略を指定することを推奨します。
// 静的データ(ビルド時に取得)
fetch(url, { cache: "force-cache" });
// 動的データ(毎回取得)
fetch(url, { cache: "no-store" });
// 時間ベースの再検証
fetch(url, { next: { revalidate: 3600 } });
まとめ
App Router は Server Components を活用することで、パフォーマンスとDXの両方を向上させることができます。まだ使っていない方は、ぜひ試してみてください。