前提
この記事では以下を前提としています。
- Next.js のバージョンは 14.0.0(この記事の内容は最低でも Next.js 13.3.0 以降が必要です。)
- Typescript 使用
この記事のゴール
この記事では Next.js で以下のような一般的なサイトマップを自動生成する手順について解説します。
<?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
を作成し、その中にサイトマップの生成に必要な関数を記述します。
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
が用意されており使用できます。それぞれ更新頻度と優先順位のためのプロパティです。
これらの値を含めて、例えば以下のように各ページの情報を返すこともできます。
// ...省略...
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
は省略しています。
該当部分を抜粋すると、以下のように記載されています。
- 他の 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 タグ定義」を参照してください。
動作確認
yarn dev
を実行して起動し、http://localhost:3000/sitemap.xml
にアクセスしてみます。冒頭に載せた以下のようなサイトマップが表示されれば正常にサイトマップが作成されていることを確認できます。
また、実際に本番環境での稼働のためにビルドして起動すれば同じようにサイトマップが作成されhttps://WebサイトURL/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 が導入されサイトマップの生成についても特にパッケージなどを導入せずともデフォルトの状態でシンプルに生成できるようになりました。 これまでサイトマップ生成のためにパッケージを導入していた方は、ぜひ公式ドキュメントに従って実装してみてください。