Next.jsで動的にsitemap.xmlを生成する

公開日時

SSGのサービスの場合はsitemap.xmlを生成するスクリプトをビルド時に実行することで、sitemap.xmlを最新に保つことが出来るが、Next.js9.3から利用可能になったIncremental Static Regenerationを用いたサービスの場合は、ビルド後にコンテンツが動的に増減するためsitemap.xmlの生成に一工夫が必要になる。

このsitemap.xmlはSSRで生成するしかないので[[pages/sitemap.xml.ts]]を作成し、getServerSideProps内でxmlを生成する。

ブログ記事(posts)を例とする。

sitemap生成時は全記事のURLが必要になるので、記事一覧取得APIを繰り返し呼び出して全記事を取得するか、sitemap専用のAPIを作る必要がある。

今回はsitemap専用のAPIを作ることにする。

↓のようにgetServerSideProps内でsitemap.xmlを組み立てて、res.writeでレスポンスを返すようにしている。

PageComponentは使っていないが、何かしらのJSXを返す必要があるので空のComponentを返すようにした。

これで記事の増減があった場合にも動的にsitemap.xmlが生成される。

// pages/sitemap.xml.ts
import { GetServerSidePropsContext } from 'next'
import React from 'react'

import { someApiClient } from '@/lib/someApiClient'
import { GetSitemapResponse } from '@/lib/GetSitemapResponse'

const createSitemap = (response: GetSitemapResponse) => {
  const siteUrl = process.env.NEXT_PUBLIC_SITE_URL

  return `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
        xmlns:xhtml="http://www.w3.org/1999/xhtml"
        xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0"
        xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"
        xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">
        ${response.posts
          .map(({ slug, publishedAt }) => {
            return `
                    <url>
                        <loc>${`${siteUrl}/posts/${slug}`}</loc>
                        <lastmod>${publishedAt}</lastmod>
                    </url>
                `
          })
          .join('')}
    </urlset>
    `
}

export const getServerSideProps = async ({ res }: GetServerSidePropsContext) => {
  const response = await someApiClient('/api/sitemap').catch((e) => {
    console.error(e.message)
    return {
      posts: [],
    }
  })

  res.setHeader('Content-Type', 'text/xml')
  res.write(createSitemap(response))
  res.end()
  return { props: {} }
}

const Sitemap = () => {
  return <></>
}

export default Sitemap

同じようにしてgetServerSideProps内でRSSを生成すればRSS配信もできる。

参考


Related #next.js

SharedArrayBuffer updates in Android Chrome 88 and Desktop Chrome 92

クロスオリジン分離対応を実施

react-hook-formとReact Datepickerを組み合わせる

Hook FormのControllerを使う

Next.jsで生成したサイトで特定のページのみnoindexを設定する

タグに紐づく記事一覧ページはnoindexにした

Next.jsでAdsenseタグを埋め込んだら Only one AdSense head tag supported per page エラーが発生

Only one AdSense head tag supported per page. The second tag is ignored.