Loading...

2023-11-13(月) 15:00

🌏 Next.js でサイトマップを自動生成する

Next.js
この記事ではNext.jsのバージョン13以降でパッケージを使わずにサイトマップを生成するための公式の手順について解説します。

目次

前提

この記事では以下を前提としています。

  • Next.js のバージョンは 14.0.0(この記事の内容は最低でも Next.js 13.3.0 以降が必要です。)
  • Typescript 使用

この記事のゴール

この記事では Next.js で以下のような一般的なサイトマップを自動生成する手順について解説します。

sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://ritaiz.com/</loc>
    <lastmod>2023-08-01T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles</loc>
    <lastmod>2023-08-01T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles/how-to-deploy-supabase-edge-function-for-stripe</loc>
    <lastmod>2023-09-11T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles/how-to-implement-csv-import-feature-in-nextjs</loc>
    <lastmod>2023-09-25T06:00:00.000Z</lastmod>
  </url>
 
  <!-- 省略 -->

以下の Next.js(App Router 使用) の公式ドキュメントに従います。

sitemap.xml

Add or generate a sitemap.xml file that matches the Sitemaps XML format in the root of app directory to help search engine crawlers crawl your site more efficiently.

nextjs.org

sitemap.ts の作成

Next.js の公式ドキュメントに従い、appディレクトリ直下にsitemap.tsを作成し、その中にサイトマップの生成に必要な関数を記述します。

app/sitemap.ts
import { MetadataRoute } from 'next';
// 私の環境では'@/lib/articles'の中に投稿記事一覧を取得できる関数が定義されている前提で
// 以下のようにimportしています。
import { getArticles } from '@/lib/articles';
 
// Article(投稿記事)の型を以下のようにします。
// これはあくまで私が想定している型なので、
// 各自が使用している環境に合わせて変更してください。
interface Article {
  title: string;
  slug: string;
  description?: string;
  createdAt: string;
  image?: string;
}
 
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const baseURL = process.env.NEXT_PUBLIC_URL || ''; // .env
  const _lastModified = new Date();
 
  // 投稿記事一覧を取得
  // 投稿記事の一覧を取得する方法はそれぞれの環境で異なるため、ここでは割愛します。
  // ここでは、getArticles関数で記事一覧を取得できる前提で以下のようにしています。
  const articles = await getArticles();
 
  // トップページ、プライバシーページなどの静的ページ
  const staticPaths = [
    {
      url: `${baseURL}`,
      lastModified: _lastModified,
    },
    {
      url: `${baseURL}/articles`,
      lastModified: _lastModified,
    },
    {
      url: `${baseURL}/cases`,
      lastModified: _lastModified,
    },
    {
      url: `${baseURL}/privacy`,
      lastModified: _lastModified,
    },
    {
      url: `${baseURL}/cookie-policy`,
      lastModified: _lastModified,
    },
  ];
 
  // 投稿記事のような動的ページ
  const dynamicPaths = articles.map((item: Article) => {
    return {
      url: `${baseURL}/articles/${item.slug}`,
      lastModified: new Date(item.createdAt),
    };
  });
 
  // 静的ページと動的ページを合わせたものを返す
  return [...staticPaths, ...dynamicPaths];
}

上記は、各ページのurlとlastModifiedを返します。 今回は静的なページは少ないので、staticPathsに直接記述していますが、静的ページも多い場合は何かしらの方法で取得するようにするのが良いかもしれません。 ブログ記事のような動的ページは、各自の環境で定義した関数(上記だとgetArticles)で取得し、それらの情報をもとにdynamicPathsに格納しています。

サイトマップのプロパティについて

Next.js の公式ドキュメントには、各ページのプロパティとしてchangeFrequencyとpriorityが用意されており使用できます。それぞれ更新頻度と優先順位のためのプロパティです。 これらの値を含めて、例えば以下のように各ページの情報を返すこともできます。

app/sitemap.ts
// ...省略...
 
const staticPaths = [
  {
    url: `${baseURL}`,
    lastModified: _lastModified,
    changeFrequency: 'yearly',
    priority: 1.0,
  },
  {
    url: `${baseURL}/articles`,
    lastModified: _lastModified,
    changeFrequency: 'daily',
    priority: 0.9,
  },
  {
    url: `${baseURL}/cases`,
    lastModified: _lastModified,
    changeFrequency: 'monthly',
    priority: 0.7,
  },
  {
    url: `${baseURL}/privacy`,
    lastModified: _lastModified,
    changeFrequency: 'yearly',
    priority: 0.3,
  },
  {
    url: `${baseURL}/cookie-policy`,
    lastModified: _lastModified,
    changeFrequency: 'yearly',
    priority: 0.3,
  },
];
 
// ...省略...
 
// 投稿記事のような動的ページ
const dynamicPaths = articles.map((item: Article) => {
  return {
    url: `${baseURL}/articles/${item.slug}`,
    lastModified: new Date(item.createdAt),
    changeFrequency: 'monthly',
    priority: 0.5,
  };
});
 
// ...省略...

ただ、Google は公式ドキュメントの中で、priorityとchangeFrequencyの値を無視すると記述しています(以下リンクの「XML サイトマップに関するその他の注意事項」内)。そのため今回はpriorityとchangeFrequencyは省略しています。

サイトマップの作成と送信

このページでは、サイトマップの作り方、Google で利用できるようにする方法について説明します。

developers.google.com

該当部分を抜粋すると、以下のように記載されています。

  • 他の XML ファイルと同様に、すべてのタグ値をエスケープする必要があります。
  • Google は、<priority>  と  <changefreq>  の値を無視します。
  • Google は、<lastmod>  値が一貫して正確であることを(ページの最終更新との比較などにより)検証できる場合に、この値を使用します。

なお、priorityは 0.0 から 1.0 の間の値を取り、1.0 が最も優先度が高いことを表します。 changeFrequencyは、always、hourly、daily、weekly、monthly、yearly、neverのいずれかの値を指定できます。 priorityとchangeFrequencyの詳細については、以下のサイトマップの XML 形式の公式ドキュメント内の「XML タグ定義」を参照してください。

サイトマップの XML 形式

このドキュメントでは、サイトマップ プロトコルの XML スキーマについて説明します。

sitemaps.org

動作確認

yarn devを実行して起動し、http://localhost:3000/sitemap.xmlにアクセスしてみます。冒頭に載せた以下のようなサイトマップが表示されれば正常にサイトマップが作成されていることを確認できます。 また、実際に本番環境での稼働のためにビルドして起動すれば同じようにサイトマップが作成されhttps://WebサイトURL/sitemap.xmlにアクセスすれば表示されるはずです。

sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://ritaiz.com/</loc>
    <lastmod>2023-08-01T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles</loc>
    <lastmod>2023-08-01T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles/how-to-deploy-supabase-edge-function-for-stripe</loc>
    <lastmod>2023-09-11T06:00:00.000Z</lastmod>
  </url>
  <url>
    <loc>https://ritaiz.com/articles/how-to-implement-csv-import-feature-in-nextjs</loc>
    <lastmod>2023-09-25T06:00:00.000Z</lastmod>
  </url>
 
  <!-- 省略 -->

また、念のために Google Search Console などでサイトマップの送信を行い、正常に処理されるか確認することをおすすめします。

まとめ

Next.js のバージョン 13.3.0 以降 では App Router が導入されサイトマップの生成についても特にパッケージなどを導入せずともデフォルトの状態でシンプルに生成できるようになりました。 これまでサイトマップ生成のためにパッケージを導入していた方は、ぜひ公式ドキュメントに従って実装してみてください。