Adding Schema Data to Next.js Sites

Paul Grieselhuber

Paul Grieselhuber

Apr 25, 2019

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.

Paul Grieselhuber

Paul Grieselhuber

Founder, President

Paul has extensive background in software development and product design. Currently he runs rendr.

Book a discovery call with our product experts.

Our team of web and mobile application experts look forward to discussing your next project with you.

Book a call 👋