Adding Schema Data to Next.js Sites

Cover Image for Adding Schema Data to Next.js Sites
Posted: under React
Reading time: 2 min read

In the same spirit of last week's post about adding citations to Schema.org data, this post provides a quick and easy method for adding Schema.org data to a Next.js site.

In this project I am using Apollo to pull data via GraphQL from a Wordpress site (which ends up generating a static HTML site) - so assume standard WP post data structure. This method assumes you have called the Schema component in your Post (or similar) component in your Next.js app. So, call the Schema component as follows:

<Schema post={post} />

And the Schema component itself:

import Head from "next/head";
import { site, siteTitle } from "../../config";

function strip(html) {
  const one = html.replace(/<\/?[^>]+(>|$)/gm, "");
  const two = one.replace(/[\r\n]\s*[\r\n]/gm, "");

  return two;
}

const Schema = ({ post }) => {
  const {
    title,
    blurb,
    featuredImage,
    date,
    modified,
    slug,
    commentCount,
    author,
    ratingCount,
    ratingAverage,
    citations,
  } = post;
  const published = new Date(date);
  const copyrightYear = published.getFullYear();

  let mediaDetails, sourceUrl;

  if (featuredImage) {
    sourceUrl = featuredImage.sourceUrl;
  }

  const citationsList = citations.map((citation, i) => {
    return `{ "@type": "CreativeWork", "citation": ${JSON.stringify(
      citation
    )} }${i === citations.length - 1 ? "" : ","}\n`;
  });

  const citationsText = citationsList.join("");

  const org = `{ "@id": "${site}#organization", "type": "Organization", "name":"${siteTitle}", "logo": {
    "@type": "ImageObject",
    "name": "${siteTitle} Logo",
    "width": "230",
    "height": "67",
    "url": "${site}images/logo.png"
} }`;

  return (
    <Head>
      <script type="application/ld+json">{`
    {
      "@context":"https://schema.org/",
      "@type":"Article",
      "name":"${title}",
      ${
        ratingAverage > 4
          ? `"aggregateRating": {
        "@type":"AggregateRating",
        "ratingValue":${ratingAverage},
        "reviewCount":${ratingCount}
      },`
          : ""
      }
      "about": "${blurb}",
      "author": { "@type": "Person", "@id": "${site}author/${
        author.slug
      }", "name": "${author.name}" },
      ${
        citationsText.length
          ? `"citation": [
        ${citationsText}
      ],`
          : ""
      }
      "commentCount": ${commentCount},
      "copyrightHolder": { "@id": "${site}#organization" },
      "copyrightYear": ${copyrightYear},
      "datePublished": "${date}",
      "dateModified": "${modified}",
      "description": "${blurb}",
      "discussionUrl": "${site}articles/${slug}#comments",
      "editor": { "@id": "${site}author/${author.slug}#author" },
      "headline": "${title}",
      ${sourceUrl ? `"image": "${sourceUrl}",` : ""}
      "inLanguage": "English",
      "mainEntityOfPage": "${site}articles/${slug}",
      "publisher": { "@id": "${site}#organization" },
      "sourceOrganization": ${org},
      "url": "${site}articles/${slug}"

    }
    `}</script>
    </Head>
  );
};

export default Schema;

You'll see a strip function that I left in place in the example. I am trying to decide whether or not to include the post content in the Schema data. If so, that function is used to clean up the HTML returned as content, so that we can include it in the JSON response.

Here is a gist of this snippet if you have any ideas on how to improve. PRs of course welcome.

Let's talk. We can help grow your business.

Your company is already off to a running start and you are ready for some serious growth. You're seeking the right web technology partner to propel your business forward. You've come to the right place.


Tell us a bit about your business.