Adding Schema Data to Next.js Sites

April 25, 2019
By Paul Grieselhuber

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) {
  var one = html.replace(/<\/?[^>]+(>|$)/gm, "");
  var 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.

Paul Grieselhuber