Next.jsのSSG入門:getStaticProps・動的ルート・静的出力を解説
Next.jsのPages RouterでSSGを実装する方法を解説。getStaticPropsとgetStaticPaths、動的ルート、静的出力、データ更新とISRの違いを学べます。
SSGは、ページに必要なデータを事前に集めてHTMLを生成する方法です。Pages Routerでは、ページからgetStaticPropsをexportすると、その戻り値を使った事前生成ができます。公開記事のように、多くの利用者へ同じ内容を配信するページに向いています。
この記事はTypeScriptとPages Routerを前提にしています。App RouterのgenerateStaticParamsやServer ComponentsとはAPIが異なります。プロジェクト作成はNext.jsをインストールを参照してください。
Pages Routerで静的ページを生成する
getStaticPropsで生成時の値を渡す
src/pages/build-info.tsxへ次のコードを保存します。
import type { GetStaticProps } from 'next';
type Props = {
buildTime: string;
};
export const getStaticProps: GetStaticProps<Props> = async () => {
return {
props: { buildTime: new Date().toISOString() },
};
};
export default function BuildInfoPage({ buildTime }: Props) {
return (
<main>
<h1>静的に生成したページ</h1>
<p>生成日時: <time dateTime={buildTime}>{buildTime}</time></p>
</main>
);
}
Dateオブジェクトをそのまま渡さず、文字列に変換している点が重要です。返すpropsはJSONとして扱える値にし、秘密のAPIキーや個人情報を含めません。ページへ渡したデータはブラウザから確認できます。
データ取得が不要なページは、getStaticPropsを書かなくても自動的に静的最適化の対象になることがあります。この関数は、ビルド時のデータ取得を明示する必要があるときに使います。
動的URLはgetStaticPathsで列挙する
記事ごとにURLを作る場合、src/pages/posts/[slug].tsxのような動的ルートを使います。次の例は外部APIなしで実行できるよう、2件のデータを同じファイル内に定義しています。
import type { GetStaticPaths, GetStaticProps } from 'next';
type Post = { slug: string; title: string; body: string };
type Params = { slug: string };
type Props = { post: Post };
const posts: Post[] = [
{ slug: 'hello', title: '最初の記事', body: 'SSGを試します。' },
{ slug: 'routing', title: 'URLの設計', body: '動的ルートを使います。' },
];
export const getStaticPaths: GetStaticPaths<Params> = async () => ({
paths: posts.map((post) => ({ params: { slug: post.slug } })),
fallback: false,
});
export const getStaticProps: GetStaticProps<Props, Params> = async ({ params }) => {
const post = posts.find((item) => item.slug === params?.slug);
if (!post) return { notFound: true };
return { props: { post } };
};
export default function PostPage({ post }: Props) {
return (
<main>
<h1>{post.title}</h1>
<p>{post.body}</p>
</main>
);
}
getStaticPathsは生成するURLを決め、getStaticPropsは各URLのデータを決めます。fallback: falseでは、列挙していないURLに対して404を返します。静的出力ではこの方式を使います。
実際のCMSとつなぐ場合も、ビルド中に記事一覧と本文を取得する流れは同じです。getStaticPropsから同じアプリのAPI RouteへHTTPアクセスする必要はなく、CMSクライアントなどのサーバー用処理を直接呼べます。
out/へHTMLを出力する
静的Webサーバーへ配置するなら、next.config.jsに次を設定します。既存の設定がある場合は必要な項目を統合してください。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
trailingSlash: true,
};
module.exports = nextConfig;
そのうえで、プロジェクトのルートからビルドします。
npm run build
この例では、out/build-info/index.html、out/posts/hello/index.html、out/posts/routing/index.htmlなどが生成されます。out/_next/のCSSやJavaScriptも必要なので、HTMLだけでなくout/の内容をまとめて配信先へ配置します。存在しないURLを404へ送る設定などは、配信先のWebサーバーでも確認します。
現在の静的出力はoutput: 'export'とnext buildを使います。古い手順にある独立したnext exportコマンドを、新しいバージョンへそのまま適用しないでください。
開発環境とISRの違い
npm run devでは、getStaticPropsはリクエスト時にも実行されます。そのため、開発中にページを更新するたび日時が変わっても、静的生成が失敗しているわけではありません。本番の挙動はビルド後のファイルで確認します。
Node.jsサーバーなどを使う配信構成では、revalidateを指定したISRで公開後の再生成ができます。一方、output: 'export'で配置したファイルは、サーバーがNext.jsのコードを実行しないためISRを利用できません。本文を更新したら、再ビルドと再配置が必要です。
運用で確認すること
- 静的出力では
getServerSideProps、Pages RouterのAPI Routes、fallback: trueや'blocking'を使わない。 next/imageのデフォルト最適化にはサーバーが必要なので、静的出力ではカスタムloaderやunoptimizedなどを検討する。- CMSが取得できなかったときに空の記事を公開するのか、ビルドを失敗させるのかを決める。公開本文なら失敗を検知できる設計が扱いやすい。
- 記事数の増加に応じて、取得件数の上限、ページ分割、APIの呼び出し制限を確認する。
- 認証が必要な利用者別データを、公開HTMLへ混ぜない。
SSGでも、表示後にブラウザで検索やフォーム操作を実装できます。ただし、初期HTMLに必要な本文までブラウザの取得待ちにすると、事前生成の利点を活かしにくくなります。
関連記事と公式資料
リクエストごとの処理が必要な場合はSSRの設定と注意点、仕組みの全体像はNext.jsについてで確認できます。
CMSの記事更新が表示されないとき
生成した時点のデータを確認する
静的出力では、CMSを更新しただけで公開ファイルが書き換わるわけではありません。最新データを取得して再ビルドし、出力を公開先へ反映したか確認します。
動的URLの生成漏れを調べる
記事一覧に存在していても、詳細ページのパスが生成されていなければアクセスできません。getStaticPathsで返すIDと、実際のURL・公開ファイルを照合してください。