😺

Next.js14のSSG / SSR / ISR の書き方(コンポーネント単位)

に公開

Next.js(App Router)の SSG / SSR / ISR の決め方を、dynamicrevalidate を中心にサクッとまとめます。最後に「どこへ書くべきか」も整理します。

まず結論(チートシート)

SSG(静的生成・ビルド or 初回アクセス時に生成)

  • 最も安全な指定(厳密に静的だけ許可)

    export const dynamic = 'error';      // 動的要素が混じったらエラーにする
    export const revalidate = false;     // 無期限キャッシュ(再検証しない)
    
  • 穏やかな指定(静的にしたい)

    export const dynamic = 'force-static';
    // revalidate は省略 or false(= 無期限) or 数値で ISR に切替(下で解説)
    
  • 実務メモ

    • headers() / cookies() / fetch(..., { cache: 'no-store' }) を使うと静的にできません。
    • 動的ルートは generateStaticParams を用意すると SSG できます。

SSR(毎リクエスト動的レンダリング)

  • 明示的に強制

    export const dynamic = 'force-dynamic';
    
  • もしくは、以下のいずれかを使うと自動的に SSR 扱い

    export const revalidate = 0;                 // 実質 no-store
    await fetch(url, { cache: 'no-store' });     // キャッシュしない
    import { headers, cookies } from 'next/headers'; // リクエスト依存
    
  • 実務メモ

    • revalidate = 0SSR(毎回再生成) と同義。
    • dynamic = 'force-dynamic' を書いたら revalidate の数値指定は無視されます(常に SSR)。

ISR(静的+バックグラウンド再生成)

  • ルート単位で指定

    export const revalidate = 60; // 60秒ごとにバックグラウンドで再生成
    // dynamic は 'auto'(デフォルト)のままでOK。SSGベースで期限切れ後に再生成。
    
  • 取得単位(fetch 単位)で指定

    await fetch(url, { next: { revalidate: 60 } });
    
  • dynamic = 'force-static'revalidate = 60 を併用して
    静的前提+定期再生成」をより明示にするのもアリ。

※ご指摘のコメントにあった
revalidate = false は「動的寄り」 は誤りです。
false は “無期限キャッシュ=静的寄り(再検証なし)” です。
“動的寄り”にしたい場合は revalidate = 0dynamic = 'force-dynamic' を使います。


どこに書くべき?

  • 書ける場所page.tsxlayout.tsx(サーバーコンポーネントのファイル)および Route Handler(route.ts

    • ルートセグメントごとの設定です。
    • 下位で上書き可能:親 layout.tsx の設定は、子 layout.tsx / page.tsx の設定で上書きされます。
  • 書けない場所:ふつうのコンポーネントファイル(特にクライアントコンポーネント)

    • 代わりに fetch のオプションcache: 'no-store'next: { revalidate: N })で振る舞いを局所制御します。
  • おすすめ運用

    • ルート全体の方針は 最上位(または該当セクションの)layout.tsx に置く。
    • 特定ページだけ振る舞いを変えたいときは その page.tsx に書いて 最も近い宣言を優先させる。
    • ひとつのページ内で更に細かく制御したい場合は fetch 側で指定

代表的なレシピ

1) 完全 SSG(厳格)

// page.tsx または layout.tsx
export const dynamic = 'error';
export const revalidate = false;
// 動的API(headers/cookies/no-store fetch)を使うとビルド時にエラー

2) 通常 SSG(緩め・静的優先)

export const dynamic = 'force-static';
// revalidate は未指定 or false

3) ISR(60秒)

export const revalidate = 60;
// or fetch 単位で next: { revalidate: 60 } を指定

4) SSR(毎回生成)

export const dynamic = 'force-dynamic';
// もしくは
export const revalidate = 0;
// もしくは
await fetch(url, { cache: 'no-store' });

つまずきやすいポイント

  • revalidate = false永続キャッシュ(静的寄り)
    「最新を必ず表示したい」は revalidate = 0force-dynamic
  • dynamic = 'force-dynamic' を付けると revalidate の数値指定は意味がなく なります(常時 SSR)。
  • headers() / cookies() を呼ぶだけで 動的扱い。SSG を狙うルートでは 使わない
  • 動的ルートで SSG したいなら generateStaticParams を用意。未知パラメータを 404 にするなら export const dynamicParams = false

Discussion