Next.js の App Router では、ページが静的に生成されるのか、リクエストごとにサーバーで描画されるのかを、フレームワークが自動で判断します。ただ「このページは必ず毎回サーバーで描画したい」「60秒ごとに再生成したい」といった指定を明示したい場面もあります。そのときに使うのが Route Segment Config で、page.tsx や layout.tsx から特定の名前の変数を export するだけで挙動を切り替えられます。この記事では、よく使う dynamic / revalidate / runtime などの設定値と、それぞれの意味・使いどころを解説します。
目次
Route Segment Config とは(export するだけで効く設定)
Route Segment Config は、ルートセグメント(app 以下のフォルダ1階層)ごとの振る舞いを指定する仕組みです。設定ファイルを別に用意するのではなく、page.tsx・layout.tsx・route.ts の中で決められた名前の定数を export することで有効になります。
// このページは 60 秒ごとに再生成する
export const revalidate = 60;
export default async function NewsPage() {
const res = await fetch('https://example.com/api/news');
const news = await res.json();
return (
<ul>
{news.map((item: { id: number; title: string }) => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
}
大切なルールが2つあります。1つは、これらの値はビルド時に静的に解析されるため、必ず定数でなければならないこと。export const revalidate = 60 は動きますが、export const revalidate = getInterval() のように変数や関数の結果を入れることはできません。もう1つは、レイアウトに書いた設定はその配下のページにも影響しますが、より深いセグメントで指定した設定が優先されるという点です。
dynamic:静的生成と動的レンダリングを切り替える
もっとも使う機会が多いのが dynamic です。既定値は 'auto' で、Next.js が「cookies() や headers() を使っているか」「キャッシュしない fetch があるか」などを見て、静的にできるものは静的にします。この自動判定を上書きしたいときに指定します。
| 値 | 挙動 |
|---|---|
'auto'(既定) | 可能な範囲で静的化し、必要に応じて動的になる |
'force-dynamic' | 常にリクエストごとにサーバーで描画する(キャッシュを使わない) |
'force-static' | 強制的に静的化する。cookies() や headers() は空の値を返す |
'error' | 静的化を強制し、動的な API を使っていたらビルドエラーにする |
// ログイン中のユーザーごとに内容が変わるので、常に動的に描画する
export const dynamic = 'force-dynamic';
export default async function DashboardPage() {
const res = await fetch('https://example.com/api/me');
const me = await res.json();
return <p>{me.name} さんのダッシュボード</p>;
}
'error' は「このページは絶対に静的でいてほしい」という意思表示に使えます。うっかり cookies() を呼ぶコードが混ざったとき、本番で気付くのではなくビルドで落ちてくれるので、静的配信を前提にしたサイトでは有効です。
revalidate:再生成の間隔を秒で指定する
revalidate は、生成済みのページやデータを何秒間そのまま使い回すかを指定します。指定した秒数を過ぎたあと最初にアクセスがあると、いったん古い内容を返しつつ裏側で再生成が走り、次のアクセスから新しい内容になります(いわゆる ISR)。
| 値 | 挙動 |
|---|---|
false(既定) | 無期限にキャッシュする(実質的に静的生成) |
0 | 毎回動的に描画する |
| 数値(秒) | その秒数ごとに再生成する |
注意したいのは、ページ側の revalidate と個々の fetch に指定した next.revalidate が両方ある場合、短い方が採用されるという点です。ページ全体を 3600 秒にしていても、中に 60 秒指定の fetch があればページは 60 秒ごとに再生成されます。
export const revalidate = 3600; // 1時間
export default async function BlogPage() {
// 個別に 60 秒を指定すると、ページ全体も 60 秒間隔になる
const res = await fetch('https://example.com/api/posts', {
next: { revalidate: 60 },
});
const posts = await res.json();
return <p>記事数: {posts.length}</p>;
}
dynamicParams:generateStaticParams にないパスの扱い
動的ルート([slug] など)で generateStaticParams を使っているとき、そこに含まれないパスへアクセスされた場合の挙動を決めるのが dynamicParams です。既定は true で、リストになかったパスも要求されたときにサーバーで生成します。false にすると、リストにないパスは 404 になります。
// ビルド時に用意したページ以外は 404 にする
export const dynamicParams = false;
export async function generateStaticParams() {
return [{ slug: 'getting-started' }, { slug: 'installation' }];
}
export default async function DocPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <h1>{slug}</h1>;
}
ドキュメントサイトのように「ページの一覧がビルド時に確定している」ケースでは false が向きます。逆に、CMS で記事が追加され続けるブログなどは既定の true のままにして、新しい記事にアクセスがあったときに生成させる形が便利です。
runtime と maxDuration
runtime は、そのセグメントを実行する環境を指定します。既定は 'nodejs' で、Node.js の API がひととおり使えます。'edge' にすると起動が速いエッジランタイムで動きますが、使える API が Web 標準のものに限られ、多くの Node.js 向けライブラリが動かない点に注意が必要です。
maxDuration は、サーバー側の処理に許される最大秒数です。指定できる範囲や既定値はデプロイ先のプラットフォームによって決まるため、上限を超える値を書いても効きません。時間のかかる処理を含むルートで、プラットフォームの既定より長い時間を確保したいときに使います。
export const runtime = 'nodejs'; // 'nodejs' | 'edge'
export const maxDuration = 30; // 秒(対応はデプロイ先による)
export async function GET() {
const data = await buildHeavyReport();
return Response.json(data);
}
設定が効いていないように見えるとき
開発モードで確認している
next dev では、変更をすぐ画面に反映するためにキャッシュの挙動が本番と異なります。revalidate を設定しても毎回再取得されるように見えることがあるので、キャッシュまわりの確認は next build と next start で行ってください。next build の出力には、各ルートが静的(○)か動的(ƒ)かの記号が並ぶので、意図どおりになっているかを確かめられます。
変数や関数の結果を代入している
冒頭でも触れたとおり、これらの設定はビルド時に静的解析されます。export const revalidate = Number(process.env.REVALIDATE) のような書き方は解析できず、エラーになるか無視されます。必ずリテラルの値を直接書いてください。
クライアントコンポーネントに書いている
Route Segment Config が意味を持つのは page.tsx・layout.tsx・route.ts といったルートを構成するファイルです。'use client' を付けた普通のコンポーネントファイルで export const revalidate = 60 と書いても、単なる定数の export として扱われるだけで何も起きません。
親のレイアウトの設定に引っ張られている
レイアウトに書いた設定は配下のページにも及びます。「特定のページだけ動的にしたい」つもりでも、上位のレイアウトで dynamic = 'force-static' が指定されていると意図が食い違います。挙動がおかしいと感じたら、そのページだけでなく親のレイアウトも確認しましょう。より深いセグメントの指定が優先されるので、ページ側で明示的に上書きすれば解決します。
まとめ
Route Segment Config は、page.tsx や layout.tsx から決まった名前の定数を export するだけで、そのルートのレンダリング方式やキャッシュの挙動を指定できる仕組みです。dynamic は静的化と動的レンダリングの切り替えで、常にサーバーで描画したいなら 'force-dynamic'、静的を保証したいなら 'error' が使えます。revalidate は再生成の間隔を秒で指定し、個々の fetch に指定した値と競合したときは短い方が採用されます。dynamicParams は generateStaticParams にないパスを生成するか 404 にするかの切り替え、runtime と maxDuration は実行環境と処理時間の指定です。いずれもビルド時に静的解析されるためリテラルで書くこと、レイアウトの設定が配下に影響すること、キャッシュの確認は本番モードで行うことを覚えておくと、想定外の挙動に悩まされにくくなります。