2023/08/30
Next.js(App Router)で構造化データ(JSON-LD)に対応する
Next.js(App Router)で作ったブログの構造化データをschama.org + JSON-LDで記述した際の記録です
ブログ記事用JsonLDコンポーネント
今回は「ブログ記事(BlogPosting)」と「パンくずリスト(BreadcrumbList)」に対応した構造化データを記述していきます。
import { Post } from "@/.contentlayer/generated";
import { siteConfig } from "@/config/siteConfig";
import Script from "next/script";
export const JsonLD = ({ post }: { post: Post }) => {
const url = `${process.env.NEXT_PUBLIC_URL}${post.slug}`;
const tag = post.tags ? post.tags[0] : "";
const jsonld = [
{
"@context": "https://schema.org",
"@type": "BlogPosting",
mainEntityOfPage: {
"@type": "WebPage",
"@id": url,
},
headline: post.title,
datePublished: post.date,
dateModified: post.update ? post.update : post.date,
keywords: post.tags,
description: post.description,
image: [siteConfig.og],
publisher: {
"@type": "Organization",
name: siteConfig.name,
},
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
name: "パンくずリスト",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: siteConfig.name,
item: siteConfig.url,
},
{
"@type": "ListItem",
position: 2,
name: tag,
item: `${process.env.NEXT_PUBLIC_URL}/tags/${tag}`,
},
{
"@type": "ListItem",
position: 3,
name: post.title,
item: url,
},
],
},
];
return (
<>
<Script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonld) }}
/>
</>
);
};
補足: @type
の種類
今回は「記事(BlogPosting)」と「パンくずリスト(BreadcrumbList)」のスキーマタイプを使用しましたが、他にもなんと803種類あるらしいです。
一般的なwebサイトで使いそうなのはこの辺ですかね。
- BlogPosting(Article) ... ブログ記事
- Breadcrumbs ... パンくずリスト
- Product ... 商品情報
- FAQ ... FAQページ
- Recipe ... レシピ
- LocalBusiness ... 店舗情報
- Organization ... 会社情報
公式サイトがちょっと見にくいんですが、こちらのページの中でLook up a term using the TermFinderと書いてある隣のinputからスキーマタイプの検索ができます。それぞれのスキーマタイプによって設定できるプロパティが変わるので、こちらから自分が使いたいタイプを確認してみてください。
記事ページ
JSON-LDはbody内に書いても問題ないそうなので、先ほど作ったコンポーネントをpage.tsx内で呼び出すことにしました。
app/[slug]/page.tsx
import { Metadata } from "next";
import { Post, allPosts } from "contentlayer/generated";
import { JsonLD } from '@/components/json-ld.tsx';
interface PageProps {
params: {
slug: string[];
};
}
async function getPageFromParams(params: PageProps["params"]) {
const { slug } = params;
let _slug = slug.toString();
const page: Post | undefined = allPosts.find(
(page) => page.slug === `/${_slug}`,
);
if (!page) {
return null;
}
return page;
}
export default async function Page({ params }: PageProps) {
const page: Post | null = await getPageFromParams(params);
return (
<div className="w-[100%] pb-5 pt-3">
...
<JsonLD post={page} /> {/* ここ */}
</div>
);
}
リッチリザルトテストでちゃんと表示されていれば完了です☺️