Next.js(App Router)で構造化データ(JSON-LD)に対応する
2023/08/30

Next.js(App Router)で構造化データ(JSON-LD)に対応する

4 mins to read

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>
  );
}

リッチリザルトテストでちゃんと表示されていれば完了です☺️