
How to Add Schema Markup to WordPress Posts and Pages?
Search engines read your pages as text and links. They can guess that a page is a recipe, a product, or a news article, but guessing is unreliable. Schema markup removes the guesswork. It is structured data, written in a shared vocabulary from Schema.org, that tells search engines exactly what a page is about: this is an article, published on this date, by this author, for this organization, with this image. Google uses that data to understand pages and to show rich results, such as product prices, star ratings, breadcrumbs, and article details in search.
WordPress does not output schema markup on its own. You add it with an SEO plugin, a dedicated schema plugin, or your own code, and many sites end up with all three fighting each other.
This article covers what schema markup is and which formats and types matter, how to get schema from SEO plugins, how to add your own JSON-LD with clean and secure PHP, how to extend Yoast SEO and Rank Math schema instead of duplicating it, which rich results are still worth pursuing, and how to test and monitor your markup.
What Schema Markup Is
Schema markup describes the things on a page using types and properties from the Schema.org vocabulary. A blog post is a BlogPosting with a headline, datePublished, author, and image. A company is an Organization with a name, url, and logo. A product is a Product with an offers property containing a price.
There are three ways to embed it in HTML:
| Format | How it is added | Recommended? |
|---|---|---|
| JSON-LD | A separate script block of JSON in the page | Yes. Google's preferred format |
| Microdata | Attributes such as itemprop added to HTML elements | Works, but hard to maintain |
| RDFa | Attributes such as property added to HTML elements | Works, but rarely used today |
JSON-LD is the clear choice for WordPress. It is kept separate from your theme's HTML, so changing a template does not break it, and it can be generated entirely in PHP from post data.
A minimal JSON-LD block for a blog post looks like this:
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "How to Add Schema Markup to WordPress Posts and Pages?",
"datePublished": "2026-10-09T05:00:00+00:00",
"dateModified": "2026-10-09T05:00:00+00:00",
"author": {
"@type": "Person",
"name": "Sajjad",
"url": "https://websolutionmaster.com/authors/sajjad"
},
"image": "https://websolutionmaster.com/images/blog/2026/10/how-to-add-schema-markup-to-wordpress.png"
}
Does Schema Markup Improve Rankings?
Not directly. Google has said structured data is not a general ranking factor. What it does is make pages eligible for rich results and help search engines understand entities on your site, such as who wrote an article and which organization published it. Rich results can improve click-through rates, and clearer entity data supports the trust signals Google looks for. Treat schema as part of technical SEO, alongside the practices in WordPress SEO best practices.
Two rules from Google's guidelines matter more than any code:
- Mark up only what is visible on the page. Ratings, prices, FAQs, and authors in your schema must appear in the page content.
- Keep it accurate. Misleading markup can lead to a manual action that removes rich results for your whole site.
Which Schema Types Matter for WordPress Sites
| Type | Use it for | Rich result or benefit |
|---|---|---|
Article / BlogPosting / NewsArticle | Posts and news | Better article understanding, author and date details |
Organization | Your company or publisher | Logo and entity details for knowledge panels |
WebSite | The site as a whole, on the home page | Site name in search results |
BreadcrumbList | Page hierarchy | Breadcrumb trail instead of a raw URL |
Product with Offer | Ecommerce product pages | Price, availability, and review stars |
LocalBusiness | Businesses with a physical location | Address, hours, and map details |
Event | Events with dates and locations | Event listings |
Recipe | Recipes | Images, cook times, and ratings |
FAQPage | Pages with question-and-answer content | Limited. See below |
The FAQ and HowTo Changes
In August 2023, Google limited FAQ rich results to well-known, authoritative government and health websites, and it stopped showing HowTo rich results soon after. You can still use FAQPage markup, and other search engines and AI systems may read it, but most sites will no longer see FAQ dropdowns in Google results. Google also retires other rich result types from time to time, so check the current Search Gallery before investing effort in a type.
Option 1: Schema from an SEO Plugin
The major SEO plugins all generate JSON-LD automatically. For most sites, this covers the essentials with no code:
- Yoast SEO outputs a connected graph containing
WebSite,OrganizationorPerson,WebPage,Article,BreadcrumbList, andImageObject. You choose whether the site represents an organization or a person under Yoast SEO → Settings → Site representation, and you set default page and article types per post type under Content types. - Rank Math offers a schema builder in the post editor, with templates for Article, Product, Recipe, Event, and others, and lets you set default schema per post type in its Titles & Meta settings.
- All in One SEO includes a schema generator with a similar set of types and a custom schema option.
Set up the basics first:
- Choose Organization or Person and add the name, logo, and social profile URLs.
- Set the default article type for posts, for example Blog Post for a blog or News Article for a newsroom.
- Make sure author profiles have a display name, a biography, and an avatar, since author details appear in the schema.
- Enable breadcrumbs if your theme displays them, so the breadcrumb schema matches visible breadcrumbs.
WooCommerce adds Product schema to product pages by itself. SEO plugins and WooCommerce add-ons may extend or replace it, so test product pages after installing either. More on ecommerce setup is in how to use WordPress for e-commerce.
Option 2: Adding Your Own JSON-LD with PHP
If you do not use an SEO plugin, or need full control, generate schema yourself. Build the data as a PHP array, convert it with wp_json_encode(), and print it in the page head on the wp_head action.
The example below is a must-use plugin that outputs BlogPosting and BreadcrumbList on single posts, and WebSite and Organization on the home page:
// wp-content/mu-plugins/site-schema.php
<?php
/**
* Plugin Name: Site Schema
* Description: Outputs JSON-LD schema for posts and the home page.
*/
add_action( 'wp_head', 'acme_output_schema', 20 );
function acme_output_schema() {
$graph = array();
if ( is_front_page() ) {
$graph[] = acme_schema_organization();
$graph[] = array(
'@type' => 'WebSite',
'@id' => home_url( '/#website' ),
'url' => home_url( '/' ),
'name' => get_bloginfo( 'name' ),
'publisher' => array( '@id' => home_url( '/#organization' ) ),
);
}
if ( is_singular( 'post' ) ) {
$graph[] = acme_schema_organization();
$graph[] = acme_schema_blog_posting( get_queried_object() );
$graph[] = acme_schema_breadcrumbs( get_queried_object() );
}
if ( empty( $graph ) ) {
return;
}
$json = wp_json_encode(
array(
'@context' => 'https://schema.org',
'@graph' => $graph,
),
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_HEX_TAG
);
wp_print_inline_script_tag( $json, array( 'type' => 'application/ld+json' ) );
}
function acme_schema_organization() {
$logo_id = get_theme_mod( 'custom_logo' );
$organization = array(
'@type' => 'Organization',
'@id' => home_url( '/#organization' ),
'name' => get_bloginfo( 'name' ),
'url' => home_url( '/' ),
);
if ( $logo_id ) {
$organization['logo'] = wp_get_attachment_image_url( $logo_id, 'full' );
}
return $organization;
}
function acme_schema_blog_posting( WP_Post $post ) {
$data = array(
'@type' => 'BlogPosting',
'@id' => get_permalink( $post ) . '#article',
'mainEntityOfPage' => get_permalink( $post ),
'headline' => wp_strip_all_tags( get_the_title( $post ) ),
'description' => wp_strip_all_tags( get_the_excerpt( $post ) ),
'datePublished' => get_the_date( 'c', $post ),
'dateModified' => get_the_modified_date( 'c', $post ),
'author' => array(
'@type' => 'Person',
'name' => get_the_author_meta( 'display_name', $post->post_author ),
'url' => get_author_posts_url( $post->post_author ),
),
'publisher' => array( '@id' => home_url( '/#organization' ) ),
);
$image = get_the_post_thumbnail_url( $post, 'full' );
if ( $image ) {
$data['image'] = $image;
}
return $data;
}
function acme_schema_breadcrumbs( WP_Post $post ) {
$items = array(
array(
'@type' => 'ListItem',
'position' => 1,
'name' => __( 'Home', 'acme' ),
'item' => home_url( '/' ),
),
);
$categories = get_the_category( $post->ID );
if ( ! empty( $categories ) ) {
$items[] = array(
'@type' => 'ListItem',
'position' => 2,
'name' => $categories[0]->name,
'item' => get_category_link( $categories[0] ),
);
}
$items[] = array(
'@type' => 'ListItem',
'position' => count( $items ) + 1,
'name' => wp_strip_all_tags( get_the_title( $post ) ),
);
return array(
'@type' => 'BreadcrumbList',
'itemListElement' => $items,
);
}
Key points about this code:
- One
@graphwith@idreferences. The article'spublisherpoints to the organization by@idinstead of repeating it. This is the same connected-graph approach the major SEO plugins use, and it keeps entities consistent across pages. - Dates in ISO 8601.
get_the_date( 'c' )returns the format Google expects, including the time zone. - Plain text only.
wp_strip_all_tags()removes HTML from titles and excerpts, which may contain markup. - Safe output.
JSON_HEX_TAGencodes<and>as Unicode escapes, so content containing</script>cannot break out of the script block.wp_print_inline_script_tag(), available since WordPress 5.7, prints the tag with the attributes you pass and respects the site's script-tag filters. - A must-use plugin. Schema stays in place if you change themes. The last breadcrumb item has no
itemURL, which is valid for the current page.
Adding Schema to Pages
Pages often need different types. A contact page might use ContactPage and a local business page LocalBusiness. Extend the function with checks such as is_page( 'contact' ), and keep the properties limited to what the page actually shows.
// wp-content/mu-plugins/site-schema.php
if ( is_page( 'contact' ) ) {
$graph[] = array(
'@type' => 'ContactPage',
'@id' => get_permalink() . '#webpage',
'url' => get_permalink(),
'name' => wp_strip_all_tags( get_the_title() ),
'publisher' => array( '@id' => home_url( '/#organization' ) ),
);
}
Add this inside acme_output_schema(), before the empty( $graph ) check.
Option 3: Extending an SEO Plugin's Schema
If you already use an SEO plugin, do not print a second, competing block. Extend the plugin's graph instead, so all entities stay connected.
Yoast SEO
Yoast exposes filters for the whole graph and for each piece. The wpseo_schema_article filter changes the Article piece, for example to add properties Yoast does not output by default:
// wp-content/mu-plugins/yoast-schema-tweaks.php
<?php
/**
* Plugin Name: Yoast Schema Tweaks
*/
add_filter( 'wpseo_schema_article', function ( $data ) {
if ( is_singular( 'post' ) ) {
$data['about'] = array(
'@type' => 'Thing',
'name' => 'Structured data',
);
}
return $data;
} );
The wpseo_schema_graph filter receives the full array of graph pieces, so you can add or remove entire entities. Yoast's developer documentation lists every piece filter and the context object passed to them.
Rank Math
Rank Math passes its JSON-LD data through the rank_math/json_ld filter:
// wp-content/mu-plugins/rank-math-schema-tweaks.php
<?php
/**
* Plugin Name: Rank Math Schema Tweaks
*/
add_filter( 'rank_math/json_ld', function ( $data, $jsonld ) {
if ( is_singular( 'post' ) && isset( $data['publisher'] ) ) {
$data['publisher']['foundingDate'] = '2020';
}
return $data;
}, 99, 2 );
The structure of $data varies with your settings, so dump it with Query Monitor's logging or error_log() first and check the exact keys before modifying them. Hook names and data shapes can change between plugin versions, so confirm them in each plugin's developer documentation after major updates.
Avoiding Duplicate and Conflicting Schema
The most common schema problem on WordPress sites is too much of it. A theme outputs microdata, an SEO plugin outputs a graph, a review plugin adds its own Product, and a custom snippet adds another Article. Search engines then see two articles with different authors and dates on the same page.
To find every source:
- Open a post, view the page source, and search for
application/ld+jsonanditemtype=. - Run the page through the Schema Markup Validator and list every top-level entity.
- For each duplicate, find which plugin or theme file outputs it and disable one of them, either in that plugin's settings or by removing the code.
Many themes from before the JSON-LD era add itemprop attributes to their templates. If an SEO plugin already provides a complete graph, a theme option or a child theme can remove the old microdata. For block themes, schema in templates is rare, but plugins such as recipe and review blocks often add their own.
Testing and Monitoring Schema Markup
Always test before and after changes:
- Google Rich Results Test shows which rich results a page is eligible for and lists errors and warnings for Google-supported types. Test both the URL and pasted code for pages behind a login.
- Schema Markup Validator, maintained by Schema.org, checks markup against the full vocabulary, including types Google does not use for rich results.
- Google Search Console reports structured data issues across the whole site in its Enhancements and Shopping sections, grouped by rich result type. After a fix, use Validate Fix to ask Google to recheck affected pages.
Errors prevent a rich result. Warnings about missing recommended properties do not, but filling them in usually improves eligibility. Rich results are never guaranteed, even with perfect markup.
Common Problems and Fixes
- Rich Results Test shows two Article items. Two sources output article schema. Disable schema in the theme or remove your custom code if an SEO plugin already covers it.
- "Invalid object type for field author". The author is a plain string. Use a
PersonorOrganizationobject with aname. - Wrong publish dates in search.
datePublishedordateModifiedis missing a time zone. Useget_the_date( 'c' )and set the correct time zone under Settings → General. - Schema disappears after a theme change. The code lived in the theme's
functions.php. Move it into a must-use plugin. - JSON-LD breaks when a title contains special characters. The JSON was built with string concatenation. Always build arrays and encode with
wp_json_encode(). - FAQ rich results never appear. Since 2023, Google only shows them for a small set of authoritative government and health sites.
Schema Markup FAQ
No. You can output JSON-LD with a few lines of PHP on the wp_head action. An SEO plugin is simpler for most sites because it builds a connected graph for posts, pages, authors, and your organization automatically.
Yes for most sites. JSON-LD is Google's recommended format, it sits in its own script block instead of being spread across template HTML, and it can be generated entirely from post data, so theme changes do not break it.
Not directly. Structured data helps search engines understand your content and makes pages eligible for rich results, which can increase clicks. It is not a general ranking factor on its own.
You can, but most sites will not get FAQ rich results in Google, because they are now limited to well-known government and health websites. Only add it when the questions and answers are visible on the page.
Google has to recrawl the page before it can use new markup, which can take days or weeks. Requesting indexing for important URLs in Search Console can speed this up, but rich results are never guaranteed.
Conclusion
Schema markup gives search engines a precise description of your content, and JSON-LD is the cleanest way to add it to WordPress. For most sites, an SEO plugin provides a solid connected graph for articles, pages, authors, and your organization. When you need more control, a small must-use plugin can build the same graph from post data with wp_json_encode() and print it safely in the head. When you already use a plugin, extend its graph with filters instead of adding a competing block.
Whichever route you choose, mark up only what visitors can see, keep one source of truth per page, and test with the Rich Results Test, the Schema Markup Validator, and Search Console after every change.
Here are some useful references for going deeper on schema markup:
- Google Search Central: Introduction to structured data markup — how Google uses structured data and the supported formats.
- Google Search Central: Search Gallery — the rich result types Google currently supports.
- Schema.org: Full schema hierarchy — every type and property in the vocabulary.
- Google: Rich Results Test — test a URL or code snippet for rich result eligibility.
- Yoast Developer Portal: Schema API — filters for extending the Yoast SEO schema graph.


