App RouterのOGP設定方法まとめ [Next.js]
Next.js 13.4でStableになったApp Routerにおける、メタタグ・OGP(いわゆるSEO情報)の設定方法をまとめました。
大体のサイトで使える設定内容の例
app/layout.tsx
に書くことで、デフォルトのメタデータが設定できます。サイト全体に適用されます。
以下は、日本語のWebサイトを想定した一例となります。
const siteName= 'サイト名';
const description = 'サイトの説明';
const url = 'https://本番のドメイン';
export const metadata = {
title: {
default: siteName,
/** `next-seo`の`titleTemplate`に相当する機能 */
template: `%s - ${siteName}`,
},
description,
openGraph: {
title: siteName,
description,
url,
siteName,
locale: 'ja_JP',
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: siteName,
description,
site: '@サイト用アカウントのTwitterID',
creator: '@作者のTwitterID',
},
verification: {
google: 'サーチコンソールのやつ',
},
alternates: {
canonical: url,
},
};
// 以下略
なんとOGP、Googleのサイト確認などが一気に済みます。
faviconとOG画像はファイルを設置するだけでいい
上記の設定には、あえてfaviconやOG画像を含めていません。
faviconの設置方法
app
直下に、 icon.(ico|jpg|jpeg|png|svg)
と apple-icon.(jpg|jpeg|png|svg)
というファイル名で画像を設置してください。
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" href="/icon?<generated>" type="image/<generated>" sizes="<generated>">
<link rel="apple-touch-icon" href="/apple-icon?<generated>" type="image/<generated>" sizes="<generated>">
画像を適切なファイル名で置いただけで、faviconのメタタグが設定されます。
OG画像の設置方法
app
直下に、 opengraph-image.(jpg|jpeg|png|gif)
というファイル名で画像を設置してください。
<meta property="og:image" content="<generated>" />
<meta property="og:image:type" content="<generated>" />
<meta property="og:image:width" content="<generated>" />
<meta property="og:image:height" content="<generated>" />
画像を適切なファイル名で置いただけで、OG画像のメタタグが設定されます。 これなら画像の絶対URLを考える必要が一切なくなります。
なお、opengraph-image.tsx
を設置すれば、zennや質問箱のような動的OG画像を生成できます。詳しくは上記の記事をご覧ください。
サイトURLの設定
metadata.metadataBase is not set for resolving social open graph or twitter images, fallbacks to...
上記の設定ではmetadataBase
を書いていないため、ローカル環境でこのような警告が出るはずです。
export const metadata = {
+ // Netlifyなので指定が必要 https://docs.netlify.com/configure-builds/environment-variables/
+ metadataBase: new URL(process.env.URL ?? 'http://localhost:3000'),
// 後略
もしVercel以外にデプロイする場合は、metadataBase
にURLオブジェクトを設定してください。直書きしてしまうと、プレビューデプロイのOG画像がプロダクションに向いてしまうといった問題が発生するため、ホスティングサービスが用意する環境変数を使うことを推奨します。
なぜVercelでは必要ないかというと、https://${process.env.VERCEL_URL}
にフォールバックするからです。
文字コードとビューポート
文字コードとビューポートは不要です。以下の内容をNext.jsが自動で設定します。
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
robots.txt
app
直下に、 robots.ts
を設置してください。
テキストファイルを設置しても認識されますが、MetadataRoute.Robots
という型まで用意されていますから、どうぞTSで書いてください。
サイトマップ
app
直下に、 sitemap.ts
を設置してください。
XMLを設置しても認識されますが、MetadataRoute.Sitemap
という型まで用意されていますから、どうぞTSで書いてください。
これ以外の設定は、上記ページを参照ください。
動的に設定する場合 (MicroCMSの例)
上記の設定は、基本的に全ページで使われますが、タイトルや説明、OG画像などは動的に設定するでしょう。その場合は以下のように書きます。
import { createClient } from 'microcms-js-sdk';
/**
* みんなも `envsafe` を使ってね
* @see https://github.com/KATT/envsafe
*/
import { env } from './server-env';
import type { MicroCMSImage, MicroCMSListContent } from 'microcms-js-sdk';
export const client = createClient({
serviceDomain: env.MICROCMS_SERVICE_DOMAIN,
apiKey: env.MICROCMS_API_KEY,
});
export interface Article extends MicroCMSListContent {
title: string;
}
export const getAllArticles = async () => {
return await client.getList<Article>({
endpoint: 'article',
queries: {
limit: 1000,
fields: 'id',
orders: '-createdAt',
},
});
};
export const getArticle = async (contentId: string) => {
return await client.get<Article>({
endpoint: 'article',
contentId,
});
};
例えばMicroCMSから記事を取得するとしましょう。
パスに[articleId]
というパスパラメータがあるとします。
import { getAllArticles, getArticle } from '@/utils/micro-cms';
import { Metadata } from 'next';
export const revalidate = 600;
type Props = {
params: { articleId: string };
};
/**
* パスの事前決定
*/
export async function generateStaticParams() {
const articles = await getAllArticles();
return articles.contents.map(({ id: articleId }) => ({
articleId,
}));
}
+ /**
+ * メタデータの設定
+ */
+ export async function generateMetadata({ params }: Props): Promise<Metadata> {
+ const { title } = await getArticle(params.articleId);
+ // templateを設定しているので、サイト名は自動で付く
+ return { title };
+ }
export default async function ArticlePage({ params: { articleId } }: Props) {
const { title, body } = await getArticle(articleId);
return (
<div>
<h1>{title}</h1>
{/* 以下略 */}
</div>
);
}
こういう場合、記事ページでgenerateMetadata
を使ってください。 metadata
と違って、返り値の型指定が必要です。(next@13.1.7-canary.16
現在)
template
のおかげで、サイト名は自動で挿入されます。
型について
型指定は? と思うかもしれませんが、export const metadata
を書いた時点で、中身の型チェック・補完がされます。 これはNext.js側がapp directory用の型定義を用意しているからですが、ここらへんを実現するコードを後で読んでみたいですね。Nuxt3の自動インポートとか、TypeScriptのスマートな活用法が日々増えていてワクワクしますね。
Discussion