This is already a lot of structured data, and you haven’t even got to the point of your post!
Here’s my point. Structured data is important, really important.
I think you should know a little structured data history too. I also think you should stay on top of what’s coming next, because it’s interesting and it will impact the future of search.
I think all of the future of structured data is JSON-LD, and I think that’s what we should all be implementing in our technical releases from now.
Staying on top of the topic of structured data
A really, really good way to stay on top of ideas relevant to digital marketers and SEOs in the structured data community is to follow Aaron Bradley‘s SEOSkeptic.com.
It was a post by Aaron that really helped the penny drop on the real importance of JSON-LD. Just the opening paragraph of “Zero Blue Links: Search After Traffic” is a real eye opener and one you should consume very carefully.
I reflected on the use of Schema, or more generally, structured markup (or JSON-LD for the future) is absolutely critical to apps like Google Now and its ability to pull a large majority of card results into the wrapper.
I predicted that Google’s mobile search results may soon look very much like Google Now, just in the browser, and that to maximise the opportunity a search marketer should make the best possible use of structured data and the knowledge graph, now.
I’ve since refined that view somewhat. I’m beginning to conclude that “search” for us may soon be about something more technically diverse. We’ll be staying on top of new search interfaces that require our content to be re-purposed in an alternative presentation layer to enable access.
Think about your site’s data layer and your site’s presentation layer as separate things. Think about your website as an API endpoint that can serve a JSON data payload to search engines. Think about how a search engine may choose (even, prefer for some applications) to consume your content on that API endpoint.
I can’t help but feel that eventually we’ll be doing exactly this to make sure we have visibility across a broad range of devices; organic and social channels, like answer boxes, instant articles and cards.
So to that end, I’m pretty sure (hopeful) Aaron would agree with me.
My take on using schema and why I’m writing this post
My take on using Schema Markup (Microdata) is this: I don’t want to implement it.
It messes with my presentation layer. Getting it implemented is slow, really really slow. You have to “get buy in” to win the resource via the front end development team who don’t want to mess up their HTML to accommodate the SEO team and this crappy code. Once you have it in, *then* you have to monitor it, checking every time a release is put live. Hoping that it hasn’t broken something. Which it often has. Which means more change requests, more communicating and more hassle.
All is not lost, however. JSON-LD’s keys and values match the attribute names set out in Schema.org – so you’re actually changing very little. Just compare the Microdata markup in Organization
to its JSON-LD version in Dan’s Schema guide.
JSON-LD is where it’s at
JSON-LD is where it’s at, friends. Here’s why I think this:
- You can create a JSON payload with ease and deploy anywhere on the webpage, including the head
- Developers understand it immediately (and love it, immediately)
- JSON-LD plays nice with other markup formats, should you have to use them
- JSON-LD can be implemented outside the presentation layer – it doesn’t mess up any of your HTML
- It’s really easy to implement via PHP in WordPress
I’ve had a few objections to this, namely; it feels “weird” to duplicate data on the webpage and support is limited (breadcrumbs won’t work).
I’ve reflected on those points for hours and I think the advantages in faster release outweigh any concerns. Sure – duplication. Specifically, the ArticleBody
value for in-depth articles, for example. I still don’t think that’s a problem.
Implementing JSON-LD in WordPress
So, on that note let’s get to the point of the post. I had a lot of fun implementing JSON-LD across our homepage, author pages and posts.
Take a look in the head of my author page.
<script type="application/ld+json">
// <![CDATA[
{
"@context":"http:\/\/schema.org\/",
"name":"Richard Baxter",
"@type":"Person",
"email":"richard@builtvisible.com",
"sameAs":[
"https:\/\/twitter.com\/richardbaxter",
"http:\/\/builtvisible.com\/",
"https:\/\/www.facebook.com\/richardbaxterseo",
"https:\/\/plus.google.com\/+RichardBaxterSEO\/",
"http:\/\/uk.linkedin.com\/in\/richardbaxterseo",
"http:\/\/www.slideshare.net\/richardbaxterseo"
]
}
//]]>
</script>
Which validates in the rich snippets testing tool as follows:
Our homepage uses Organization
as follows:
<script type="application/ld+json">// <![CDATA[
{
"@context": "http:\/\/schema.org\/",
"name": "Builtvisible",
"@type": "Organization",
"logo": "https:\/\/builtvisible.com\/wp-content\/uploads\/2018\/09\/builtvisible-logo.png",
"url": "http:\/\/builtvisible.com\/",
"sameAs": [
"https:\/\/twitter.com\/builtvisible",
"https:\/\/www.facebook.com\/builtvisible",
"https:\/\/www.linkedin.com\/company\/builtvisible",
"https:\/\/plus.google.com\/+SEOgadget\/posts"
],
"contactPoint": [{
"@type": "ContactPoint",
"telephone": "+44 20 7148 0453",
"email": "hello@builtvisible.com",
"contactType": "sales"
}]
}
// ]]>
</script>
Again, validating nicely:
And finally, an article:
<script type="application/ld+json">
// <![CDATA[
{
"@context":"http:\/\/schema.org\/",
"name":"Implementing JSON-LD in WordPress",
"@type":"Article",
"author":"Richard Baxter",
"ArticleSection":"Technical",
"datePublished":"2015-05-18 18:37:17",
"Publisher":"Builtvisible",
"url":"http:\/\/builtvisible.com\/implementing-json-ld-wordpress/"
}
// ]]>
</script>
Which validates nicely, here:
Implementation
Save the following code as json-ld.php in your theme directory:
<?php
// JSON-LD for WordPress Home Articles and Author Pages
// Stuff for any page
function get_post_data() {
global $post;
return $post;
}
// This has all the data of the post/page etc
$payload["@context"] = "https://schema.org/";
// Stuff for any page, if it exists
$post_data = get_post_data();
// Stuff for specific pages
$category = get_the_category();
// This gets the data for the user who wrote that particular item
if (is_single()) {
$author_data = get_userdata($post_data->post_author);
$post_url = get_permalink();
$post_thumb = wp_get_attachment_url(get_post_thumbnail_id($post->ID));
$payload["@type"] = "Article";
$payload["url"] = $post_url;
$payload["author"] = array( "@type" => "Person", "name" =>
$author_data->display_name, );
$payload["headline"] = $post_data->post_title;
$payload["datePublished"] = $post_data->post_date;
$payload["image"] = $post_thumb;
$payload["ArticleSection"] = $category[0]->cat_name;
$payload["Publisher"] = "Builtvisible";
}
// We do all this separately so we keep the right things for organization together
if (is_front_page()) {
$payload["@type"] = "Organization";
$payload["name"] = "Builtvisible";
$payload["logo"] = "https:\/\/builtvisible.com\/wp-content\/uploads\/2018\/09\/builtvisible-logo.png";
$payload["url"] = "http:\/\/builtvisible.com\/";
$payload["sameAs"] = [
"https:\/\/twitter.com\/builtvisible",
"https:\/\/www.facebook.com\/builtvisible",
"https:\/\/www.linkedin.com\/company\/builtvisible",
"https:\/\/plus.google.com\/+SEOgadget\/"
];
$payload["contactPoint"] = [
["@type" => "ContactPoint",
"telephone" => "+44 20 7148 0453",
"email" => "hello@builtvisible.com",
"contactType" => "sales"
]
];
}
// This gets the data for the user who wrote that particular item
if (is_author()) {
// Some of you may not have all of these data points in your user profiles - delete as appropriate
// fetch twitter from author meta and concatenate with full twitter URL
$author_data = get_userdata($post_data->post_author);
$twitter_url = " https:\/\/twitter.com/";
$twitterHandle = get_the_author_meta('twitter');
$twitterHandleURL = $twitter_url . $twitterHandle;
$websiteHandle = get_the_author_meta('url');
$facebookHandle = get_the_author_meta('facebook');
$gplusHandle = get_the_author_meta('googleplus');
$linkedinHandle = get_the_author_meta('linkedin');
$slideshareHandle = get_the_author_meta('slideshare');
$payload["@type"] = "Person";
$payload["name"] = $author_data->display_name;
$payload["email"] = $author_data->user_email;
$payload["sameAs"] = array($twitterHandleURL, $websiteHandle, $facebookHandle, $gplusHandle, $linkedinHandle, $slideshareHandle);
}
?>
Obviously you’ll have to edit our details out and yours in.
Also the usual disclaimers apply, use our work, but we can’t be held responsible for anything that breaks. Do check what data you have in your author_meta – we’ve added some extra user profile fields.
Then, add the following two lines of code in header.php as follows:
<?php include('json-ld.php'); ?>
<script type="application/ld+json">
// <![CDATA[
<?php echo json_encode($payload); ?>
// ]]
</script>
So wonderful. My data is kept well away from my presentation layer. I’m a very happy man.
With all the new search result formats and devices, “search” for us may soon be about staying on top of new interfaces that require our content to be re-purposed in an alternative presentation layer. Think about that: Apple Watch doesn’t have a browser.
Our own websites may become end points that need an accessible data payload to be useful to search engines. I can’t help but feel that eventually we’ll be supplying our entire site content as JSON to make sure we still have access to a broad range of devices and organic and social channels, like answer boxes, instant articles and cards.
At Builtvisible, we’ve got a sense that the JSON-LD spec might be extended to include popular actions outside of Gmail and that publishers like ourselves will be expected to make the consumption of our content entirely possible without ever visiting the site. With actions like “buy” coming to the paid shopping model, it’s only a matter of time before the same options are presented to organic search users, and HTML begins to become an option, not a prerequisite to performing well in search.
George
Awesome share. Was always curious about JSON-LD markup, looking forward to seeing what else we can do with JSON-LD. Just implemented it on our site. Thanks for sharing.
Kevin
Awesome post guys. Have you tested/played around with implementing JSON within Tag Manager to call stuff such as Product information etc/
Richard Baxter
George + Kevin, there’s a comment in your json-ld.php file:
// JSON-LD for WordPress Home, Articles and Author Pages. Written by Pete Wailes and Richard Baxter.
Remove this as it was accidentally made outside the PHP function. I’ve updated the code here so no problems moving forward :D
Aaron Bradley
Thanks for this fine look at the future Richard (because you’re absolutely right, JSON-LD is the future – at least syntactically – of structured data markup), and for your kind words concerning my work and the Semantic Search Marketing Community.
A quick note regarding your sample Article markup. Sometime after Google rolled out its new suite of structured data pages on Google Developers, they included (without any fanfare) an article titled “Enabling Rich Snippets for Articles“. In all but name these “rich snippets” are the evolution of Google’s “in-depth articles” feature: in-depth articles continue to populate the SERPs, but without the “in-depth article” label.
The required and recommended properties for article rich snippets are at variance with those used in your sample article.
Most importantly, the required headline and image fields are missing.
headline is a bit of an odd duck. You use the fundamental and perfectly acceptable name property to declare the article’s title. Why Google should require headline either in addition to or instead of name baffled me when it came to in-depth articles, and the requirement baffles me here. Be that as it may, apparently its use is required to generate an article rich snippet.
image is also required for article rich snippet generation. At least this makes more sense than requiring headline as there’s no other equivalent property for declaring an image, and a graphic is a feature of article rich snippets.
Richard Baxter
Aaron, outstanding comments – thank you for sharing. I’ll update my code and update you soon after!
Dennis Seymour
Hi Richard!
“My take on using Schema Markup (Microdata) is this: I don’t want to implement it.”
When I got to this part of the article, I was like AMEN!
I actually hated it, so I was all “hallelujah” when JSON became possible.
I actually had an internal plugin for wordpress made.
I will be sharing it this Friday through a blog post based on a presentation I did for a local conference.
The plugin basically handles the basic ones, social profiles, logo, most corporate contacts, sitelinks Search Box etc
Haven’t gotten to the article part yet though. Still more to come :)
Wondering if you’d be interested in trying it out on a site that you aren’t actively using and maybe give me feedback to further improve it? It’s still not in the WordPress.org repository though so versions will be sent out through email for the mean time.
The current page for it is over here. If you’d like, I can send it to you directly so you dont have to sign up.
The only time I’ve run into problems with it is when a certain theme won’t work with JSON.
Thanks for this post, I will be inserting it to my blog post for this friday.
PS. I still remember reading the Zero Blue links article a while back. It also opened my eyes.
Just joined Aaron’s G+ community and it looks cool!
Gary Kirwan
Loved this article as now I can visualise the history of marking up a site with all the different codes out there. I’ve had a feeling JSON-LD was being preferred by Google when I’ve reviewed their official intro to structured data. For example this quote on this page https://developers.google.com/structured-data/schema-org
To me this is Google saying use JSON-LD for Google Knowledge Graph as a primary data input and to add extra info use schema microdata where appropriate.
Anthony Collins
Thanks for the article! I use WordPress to build all my websites so this is dead useful to me. Much appreciated!
Edgar
I would just like to say: Thank You !
JSON-LD is a beautiful standard. I am working on making the uniprot rdf data available in JSON-LD, and it is starting to look like something that a JS developer would actually use. Its still RDF, but at the same time its practical and usable JSON.
At the same time SPARQL construct returning results in JSON-LD is a really nice way to build quick data applications with just a quad store and some JS files!
Thank you!
Bilal Tahir khan
Hi,
i find this post it is very helpful for me.Thanks for sharing. You did a good job.
Keep up the good work. Have a great week ahead!
J C
Would it not – if I may say so – be more of direct use if this approach were to be taken via the functions.php angle?
Or am I missing something?
Richard Baxter
Hi JC,
Yes if you wanted to permanently bake this into a theme, you could go ahead and do this inside functions.php. But for the purpose of this post, it was easier to share this way.
J.C.
Your post has somehow kept mulling through my head on its own accord, so I’ve been experimenting a bit. Granted, with zero knowledge :P But I keep wondering about how WordPress as a platform is going to deal with the evolution of search and structured data.
WordPress already has quite a bit of trouble with the Above The Fold drama, something which increasingly is seperating the marketplace theme agencies and what some appear to call the professional agencies – but that is a debate in its own right – either way I’m getting the idea that particularly Google wants the Head for itself. So to speak.
Maybe I’m wrong, but I’d still favour the use of functions.php. Perhaps you could combine both approaches, a payload via seperate json-ld.php containing classes and structure, child theme functions.php to keep the parent theme untouched, and maybe down the road a simple options page.
That said, it’s messy (I’ve got no knowledge of php or json-ld), functions.php approach does appear to work. I haven’t found much of anything elsewhere on the topic, hence why I’m back here.
add_action(‘wp_head’,’insert_json_ld’);
function insert_json_ld (){
if (is_page() || is_single()) {
if (have_posts()) : while (have_posts()) : the_post();
$context = ‘https://schema.org’;
$type = ‘Article’;
$name = get_the_title();
$authorType = ‘Person’;
$authorName = get_the_author();
$dataPublished = get_the_date(‘Y-n-j’);
$image = the_post_thumbnail();
$articleSection = get_the_excerpt();
$articleBody = get_the_content();
$url = get_permalink();
$publisherType = ‘Organization’;
$publisherName = get_bloginfo(‘name’);
$json= “‘@context’ : ‘{$context}’,
‘@type’ : ‘{$type}’,
‘name’ : ‘{$name}’,
‘author’ : {
‘@type’ : ‘{$authorType}’,
‘name’ : ‘{$authorName}’
},
‘datePublished’ : ‘{$dataPublished}’,
‘image’ : ‘{$image}’,
‘articleSection’ : ‘{$articleSection}’,
‘articleBody’ : ‘{$articleBody}’,
‘url’ : ‘{$url}’,
‘publisher’ : {
‘@type’ : ‘{$publisherType}’,
‘name’ : ‘{$publisherName}’
}”;
echo ‘{‘.$json.’}’;
endwhile; endif;
rewind_posts();
}
}
Kate
WordPress is the most popular CMS today(if i’m not mistaken) and I regularly used this in building my website. It’s nice to know that it was also implemented in wordpress through plugins. I’m still studying the syntax and I hope this could not ruin my design in wordpress.
Ian Pritchard
That’s just about the best explanation I’ve found so far. Thanks man.
Paul
Hi Team
Ive just applied this to my website. Thank-you very much for this information.
Im just curious as to how quickly this code is picked up by https://developers.google.com/structured-data/testing-tool? Is it instantly or does it take a day or two?
Cheers
Paul
Paul
Hi again
I tried to do this in a genesis child theme and its not working. Any suggestions please on how to implement this in a WordPress – Genesis Child Theme?
Thanks in advance
Paul
Andrew
Hi,
I first want to say thanks for providing the exact implementation that I was imagining in my newbie coding mind.
I have implemented this on one of the sites that I manage and have had good success in the Structured Data Testing Tool.
I was wondering if you have an implementation for wordpress is_page() and am I on the right track in thinking this would be beneficial.
Thanks in advance!
Andrew
Guy
Thank so much for your informative post. I am new to json ld and I am bit confused by the ‘implementation’ section where you create json-ld.php section. As your article is the only article I have ever seen mention this aspect of getting jason ld to work. Mostly every article I have found on the web just mentions applying the json ld markup to the webpage.
Is this something specifically for wordpress to work?
How does this step apply to html sites, etc..
Thanks for your time!
ruchin
Hello
very knowledgeable article.
i have a query about post author :
for example if i have added a post author using script mentioned above then is it possible that fornt page , home page , categories archive and others auto display the author in that too. I am facing this issue on my website, default author is shown at all of fields. Is there is any solution.
Sam
Any update to this article?
Implemented and getting a variety of Article type errors (image, logo, dateModified, and mainEntityOfPage).
Thanks,
Ryan
@Sam Google seems to have changed their requirements for certain types such as Article. Simply fill in those fields with the appropriate data and you should be good to go.
Gezgin
Thanks a lot for this great article. When I test my site with Google, it’s giving errors only for image and logo. According to their article, image should be like this:
“image”: {
“@type”: “ImageObject”,
“url”: “https://google.com/thumbnail1.jpg”,
“height”: 800,
“width”: 800
},
And logo should be under Publisher section like this:
“publisher”: {
“@type”: “Organization”,
“name”: “Google”,
“logo”: {
“@type”: “ImageObject”,
“url”: “https://google.com/logo.jpg”,
“width”: 600,
“height”: 60
}
I’m not a coder, so I wasn’t able to make the necessary array modifications. Is it possible for someone to do please?
Thanks
Mindert Aardema
For the images, you could try this:
list($width, $height) = getimagesize( $post_thumb );
$payload[“image”] = array(
“@type” => “ImageObject”,
“url” => $post_thumb,
“height” => $height,
“width” => $width
);
Amy
Thanks @Mindert Aardema for the images code. I’m getting the same errors as Gezgin.
Where do we put this code? When I place this code into the json-ld.php document I get a 200 error while testing my schema. I’ve commented it out for now… but if you could elaborate a little bit for us non-coders that would be great! Thanks so much.
Mihai Slujitoru
@gezgin
$payload[“Publisher”] = array(
“@type” => “Organization”,
“name” => “Mihai Slujitoru”,
“logo” => array(
“@type” => “ImageObject”,
“url” => “add logo”,
“width” => “add width”,
“height” => “add height”
)
);