Next.js

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.htmlout/posts/hello/index.htmlout/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・公開ファイルを照合してください。

Next.jsとは・ルーターの違い・学習順を確認する

y.
WRITTEN BY

y_ymo10

SEの部屋で、JavaScript・TypeScript・React.js・Next.jsの開発ノートを公開しています。

ほかの開発ノートを読む →