Menu

Static Site Generation (SSG)

ページがStatic Generationを使用している場合、ページのHTMLはビルド時に生成されます。つまり、本番環境では、next buildを実行したときにページのHTMLが生成されます。このHTMLは、その後の各リクエストで再利用され、CDNによってキャッシュできます。

Next.jsでは、ページをデータの有無に関わらず静的に生成できます。それぞれのケースを見ていきましょう。

データなしの静的生成

デフォルトでは、Next.jsはデータフェッチなしでStatic Generationを使用してページを事前レンダリングします。以下は例です:

function About() {
  return <div>About</div>
}
 
export default About

このページは事前レンダリングのために外部データをフェッチする必要がないことに注意してください。このようなケースでは、Next.jsはビルド時に各ページに対して1つのHTMLファイルを生成します。

データありの静的生成

一部のページは事前レンダリングのために外部データのフェッチが必要です。2つのシナリオがあり、1つまたは両方が適用される可能性があります。各ケースで、Next.jsが提供する以下の関数を使用できます:

  1. ページのコンテンツが外部データに依存している:getStaticPropsを使用します。
  2. ページのパスが外部データに依存している:getStaticPathsを使用します(通常はgetStaticPropsと組み合わせて使用)。

シナリオ1:ページのコンテンツが外部データに依存している

: ブログページがCMS(コンテンツ管理システム)からブログ投稿のリストをフェッチする必要がある場合。

// TODO: ページの事前レンダリング前に`posts`をフェッチする必要があります(何らかのAPIエンドポイントを呼び出すことで)。
export default function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  )
}

事前レンダリング時にこのデータをフェッチするために、Next.jsでは同じファイルからgetStaticPropsというasync関数をexportできます。この関数はビルド時に呼び出され、フェッチしたデータを事前レンダリング時にページのpropsに渡すことができます。

export default function Blog({ posts }) {
  // 投稿をレンダリング...
}
 
// この関数はビルド時に呼び出されます
export async function getStaticProps() {
  // 投稿を取得するための外部APIエンドポイントを呼び出す
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // { props: { posts } }を返すことで、
  // Blogコンポーネントはビルド時に`posts`をpropsとして受け取ります
  return {
    props: {
      posts,
    },
  }
}

getStaticPropsの詳細については、データフェッチングのドキュメントを参照してください。

シナリオ2:ページのパスが外部データに依存している

Next.jsでは、動的ルートを持つページを作成できます。たとえば、pages/posts/[id].jsというファイルを作成して、idに基づいて単一のブログ投稿を表示できます。これにより、posts/1にアクセスしたときにid: 1のブログ投稿を表示できます。

動的ルーティングの詳細については、動的ルーティングのドキュメントを参照してください。

しかし、ビルド時に事前レンダリングするidは外部データに依存する可能性があります。

: データベースに1つのブログ投稿(id: 1)しか追加していない場合を想定します。この場合、ビルド時にposts/1のみを事前レンダリングしたいでしょう。

後に、id: 2の2番目の投稿を追加するかもしれません。その場合、posts/2も事前レンダリングしたくなります。

つまり、事前レンダリングするページのパスが外部データに依存しているのです。これを処理するために、Next.jsでは動的ページ(この場合はpages/posts/[id].js)からgetStaticPathsというasync関数をexportできます。この関数はビルド時に呼び出され、事前レンダリングするパスを指定できます。

// この関数はビルド時に呼び出されます
export async function getStaticPaths() {
  // 投稿を取得するための外部APIエンドポイントを呼び出す
  const res = await fetch('https://.../posts')
  const posts = await res.json()
 
  // 投稿に基づいて事前レンダリングするパスを取得
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))
 
  // ビルド時にこれらのパスのみを事前レンダリングします。
  // { fallback: false }は他のルートが404になることを意味します。
  return { paths, fallback: false }
}

pages/posts/[id].jsでは、このidに関するデータをフェッチしてページを事前レンダリングするために、getStaticPropsもエクスポートする必要があります:

export default function Post({ post }) {
  // 投稿をレンダリング...
}
 
export async function getStaticPaths() {
  // ...
}
 
// これもビルド時に呼び出されます
export async function getStaticProps({ params }) {
  // paramsには投稿の`id`が含まれます。
  // ルートが/posts/1のような場合、params.idは1になります
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  // propsを介して投稿データをページに渡します
  return { props: { post } }
}

getStaticPathsの詳細については、データフェッチングのドキュメントを参照してください。

Static Generationをいつ使用すべきか

ページを1回だけビルドしてCDNで提供できるため、サーバーが各リクエストでページをレンダリングするよりもはるかに高速になります。そのため、可能な限りStatic Generation(データの有無に関わらず)の使用をお勧めします。

以下のようなタイプのページに対してStatic Generationを使用できます:

  • マーケティングページ
  • ブログ投稿とポートフォリオ
  • Eコマース商品リスト
  • ヘルプとドキュメント

ページを「ユーザーのリクエストの前に」事前にレンダリングできるかどうかを自問すべきです。答えがイエスであれば、Static Generationを選択すべきです。

一方、ユーザーのリクエストの前にページを事前にレンダリングできない場合、Static Generationは適していません。おそらく、頻繁に更新されるデータを表示するページや、リクエストごとにページのコンテンツが変更されるページの場合です。

このような場合、以下のいずれかを行うことができます:

  • クライアントサイドデータ取得を使用したStatic Generation: ページの一部の事前レンダリングをスキップし、クライアントサイドのJavaScriptを使用してそれらを埋めることができます。このアプローチの詳細については、データ取得のドキュメントを確認してください。
  • サーバーサイドレンダリングの使用: Next.jsは各リクエスト時にページを事前レンダリングします。CDNでキャッシュできないためページの読み込みは遅くなりますが、事前レンダリングされたページは常に最新の状態になります。このアプローチについては後述します。