Last active
March 31, 2022 13:15
-
-
Save mckhames/b4f3ef74571f64e62bb0c3a980054ad0 to your computer and use it in GitHub Desktop.
How To Generate Schema-Org JSON-LD + OpenGraph Metadata with NextJS and Wordpress
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//This assumes NextJS with wordpress backend that has wpGraphql plugin installed. | |
//We're filling in all required metadata with standard wordpress fields. Recommend using ACF or equivalent to finetune the different | |
//fields, as ours will contain a few duplicates (i.e. category, subject, etc) | |
import Head from "next/head"; | |
import { format } from "date-fns"; | |
export default function MetadataComponent({ post }) { | |
//create function to strip html from content so that article body can be added to schema | |
function strip(html) { | |
var one = html.replace(/<\/?[^>]+(>|$)/gm, ""); | |
var two = one.replace(/[\r\n]\s*[\r\n]/gm, ""); | |
return two; | |
} | |
//create function to grab your tags from the post object and return them as a comma-separated string: keywords! | |
const getKeywords = () => { | |
if (post.tags) { | |
let myTags = []; | |
post.tags.edges.forEach((element) => myTags.push(element.node.name)); | |
let keywords = myTags.join(); | |
return keywords; | |
} else return ""; | |
}; | |
//get the word count for the article | |
const getWordCount = (body) => { | |
if (body) { | |
// strip html | |
let strippedHTML = strip(body); | |
//return the number of words, determined by counting the total spaces in the body | |
return strippedHTML.split(" ").length; | |
} else return ""; | |
}; | |
const articleBody = strip(post.content); | |
// setting some constants...you could & should use environment variables and/or ACF for this... | |
// if your org and site name are not the same, use schema.org/organization to understand what will work best in your situation. | |
const organizationName = "Example Site / Organization Name"; | |
const baseUrl = "https://example.com/blog/posts/"; | |
const baseAuthorUrl = "https://example.com/blog/authors/"; | |
//generate author info | |
//note that metadataAuthor includes email, which you may want to leave off. | |
const metadataAuthor = `${post.author.node.firstName} ${post.author.node.lastName}, ${post.author.node.email}`; | |
// adjust accordingly | |
// ...the author routes in this example are as such: https://example.com/blog/authors/[authorFirstName-authorLastName] | |
const authorUrl = `${baseAuthorUrl}${post.author.node.firstName.toLowerCase()}-${post.author.node.lastName.toLowerCase()}`; | |
const schemaOrgAuthor = `${post.author.node.firstName} ${post.author.node.lastName}`; | |
// generate current url | |
// ...this example generates something like: https://example.com/blog/posts/[slug] | |
// ...the slug is set in wordpress. | |
const currentUrl = `${baseUrl}${post.slug}`; | |
//general metadata setup with props | |
const title = `${post.title}`; | |
// we're assuming here that the first catgory listed is the "primary" category | |
// ... we're listing that category as the "subject" & "topic" | |
// ... good example of when creating & fetching custom fields could come in handy. | |
const subject = `${post.categories.edges[0].node.name}`; | |
const topic = `${subject}`; | |
const category = `${topic}`; | |
const description = `${post.excerpt}`; | |
const summary = `${description}`; | |
const publishedDate = `${format(new Date(post.date), "PPPppp")}`; // April 29th, 1453 at 9:30am GMT-4 | |
const revisedDate = `${format(new Date(post.modified), "PPPppp")}`; | |
const keywords = `${getKeywords()}`; | |
const wordCount = `${getWordCount(post.content)}`; | |
const featuredImageUrl = `${post.featuredImage.node.sourceUrl}`; | |
const lang = "EN"; | |
const locale = "en_US"; | |
const fbPageId = "YourFbPageId"; | |
return ( | |
<Head> | |
{/** HTML META + OPENGRAPH TAGS **/} | |
<link rel="canonical" href={currentUrl} /> | |
<meta name="title" property="og:title" content={title} /> | |
<meta property="og:type" content="article" /> | |
<meta | |
name="description" | |
property="og:description" | |
content={description} | |
/> | |
<meta name="author" content={metadataAuthor} /> | |
<meta property="og:author" content={authorUrl} /> | |
<meta name="keywords" content={keywords} /> | |
<meta name="image" property="og:image" content={featuredImageUrl} /> | |
<meta name="url" property="og:url" content={currentUrl} /> | |
<meta name="subject" content={subject} /> | |
<meta name="copyright" content={organizationName} /> | |
<meta name="language" content={lang} /> | |
<meta property="og:locale" content={locale} /> | |
<meta name="robots" content="index,follow" /> | |
<meta name="revised" content={revisedDate} /> | |
<meta name="topic" content={topic} /> | |
<meta name="summary" content={summary} /> | |
<meta name="category" content={category} /> | |
<meta name="fb:page-id" content={fbPageId} /> | |
{/** Now for the fun stuff...SCHEMA-ORG/JSON-LD Implementation **/} | |
<script | |
type="application/ld+json" | |
dangerouslySetInnerHTML={{ | |
__html: ` | |
{ | |
"@context" : "http://schema.org", | |
"@type" : "Article", | |
"name" : "${title}", | |
"headline": "${title}", | |
"wordCount": "${wordCount}", | |
"author" : { | |
"@type" : "Person", | |
"name" : "${schemaOrgAuthor}", | |
"url": "${authorUrl}" | |
}, | |
"keywords" : "${keywords}", | |
"datePublished" : "${publishedDate}", | |
"dateCreated" : "${publishedDate}", | |
"dateModified": "${revisedDate}", | |
"image" : "${featuredImageUrl}", | |
"articleSection" : "${subject}", | |
"articleBody" : "${articleBody}", | |
"url" : "${currentUrl}", | |
"publisher" : { | |
"@type" : "Organization", | |
"name" : "${organizationName}" | |
} | |
} | |
`, | |
}} | |
/> | |
</Head> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment