😺
Next.js14のSSG / SSR / ISR の書き方(コンポーネント単位)
Next.js(App Router)の SSG / SSR / ISR の決め方を、dynamic
と revalidate
を中心にサクッとまとめます。最後に「どこへ書くべきか」も整理します。
まず結論(チートシート)
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 = 0
は SSR(毎回再生成) と同義。 -
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 = 0
かdynamic = 'force-dynamic'
を使います。
どこに書くべき?
-
書ける場所:
page.tsx
・layout.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 = 0
かforce-dynamic
。 -
dynamic = 'force-dynamic'
を付けるとrevalidate
の数値指定は意味がなく なります(常時 SSR)。 -
headers()
/cookies()
を呼ぶだけで 動的扱い。SSG を狙うルートでは 使わない。 - 動的ルートで SSG したいなら
generateStaticParams
を用意。未知パラメータを 404 にするならexport const dynamicParams = false
。
Discussion