
How to Add Schema Markup to a WordPress Website?
Schema markup is structured data that spells out exactly what your content means to search engines, in a format they can parse with certainty rather than infer from surrounding text. It's what turns a plain blue search result link into a rich result with star ratings, a recipe's cook time, an FAQ dropdown, or event dates displayed directly on the results page. The vocabulary comes from Schema.org, a shared standard maintained jointly by Google, Bing, Yahoo, and Yandex.
This guide covers the JSON-LD format Google recommends, how to add several common schema types by hand, how to let a plugin generate them for you, and how to validate that your markup actually works.
JSON-LD: The Format to Use
Schema can technically be embedded three ways (JSON-LD, Microdata, RDFa), but Google explicitly recommends JSON-LD, a block of JSON placed inside a <script> tag that describes the page without touching your visible HTML at all:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "How to Add Schema Markup to a WordPress Website",
"datePublished": "2026-09-02T06:00:00Z"
}
</script>
Because it's self-contained, JSON-LD is easier to generate dynamically from PHP and far less likely to break your page's actual markup than the alternatives, which is why it's the format used throughout this guide.
Method 1: Add JSON-LD Manually via functions.php
For full control, you can output schema directly using WordPress's wp_head hook. Here's a real, working example for a basic Article schema on single blog posts:
add_action( 'wp_head', function () {
if ( ! is_single() ) {
return;
}
global $post;
$schema = [
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => 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' => [
'@type' => 'Person',
'name' => get_the_author_meta( 'display_name', $post->post_author ),
],
'publisher' => [
'@type' => 'Organization',
'name' => get_bloginfo( 'name' ),
],
'mainEntityOfPage' => [
'@type' => 'WebPage',
'@id' => get_permalink( $post ),
],
];
if ( has_post_thumbnail( $post ) ) {
$schema['image'] = wp_get_attachment_image_url( get_post_thumbnail_id( $post ), 'full' );
}
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>' . "\n";
} );
A few details worth understanding:
wp_json_encode()is used instead of rawjson_encode()since it applies WordPress's own filters and handles encoding edge cases consistently across environments.- The
is_single()check limits the schema to actual blog posts. Add similar conditionals (is_page(), a custom post type check, etc.) if you want different schema types elsewhere. - All dynamic values still pass through WordPress functions like
get_the_title()andwp_strip_all_tags(), which handle escaping appropriately for their context — you're not hand-concatenating raw strings into the output.
FAQ Schema Example
If a page includes an FAQ section (like the one at the bottom of this post), FAQ schema can make individual questions eligible to appear directly in search results:
add_action( 'wp_head', function () {
if ( ! is_page( 'faq' ) ) {
return;
}
$faqs = [
[
'question' => 'Do I need a plugin to add schema markup?',
'answer' => 'No, schema can be added manually via functions.php, but a plugin is often faster for common types.',
],
[
'question' => 'Will schema markup guarantee a rich result?',
'answer' => 'No, schema makes a page eligible for a rich result, but Google decides whether and how to display one.',
],
];
$items = array_map( function ( $faq ) {
return [
'@type' => 'Question',
'name' => $faq['question'],
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => $faq['answer'],
],
];
}, $faqs );
$schema = [
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => $items,
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>' . "\n";
} );
Note that Google significantly narrowed which sites are eligible for the FAQ rich result visual treatment in 2023, limiting it mostly to well-established government and health sites — but the schema itself remains valid and worth including regardless, since eligibility criteria have changed before and may again, and other search engines and AI-driven answer features can still make use of accurately marked-up FAQ content.
Breadcrumb Schema Example
Breadcrumb schema helps Google display a page's site hierarchy directly in search results instead of the raw URL:
add_action( 'wp_head', function () {
if ( ! is_single() ) {
return;
}
$items = [
[
'position' => 1,
'name' => 'Home',
'item' => home_url( '/' ),
],
[
'position' => 2,
'name' => 'Blog',
'item' => home_url( '/blog/' ),
],
[
'position' => 3,
'name' => get_the_title(),
'item' => get_permalink(),
],
];
$list_items = array_map( function ( $item ) {
return [
'@type' => 'ListItem',
'position' => $item['position'],
'name' => $item['name'],
'item' => $item['item'],
];
}, $items );
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $list_items,
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>' . "\n";
} );
LocalBusiness Schema Example
If you run a business with a physical location or defined service area, LocalBusiness schema helps it appear correctly in local search results and Google Business Profile-linked features:
add_action( 'wp_head', function () {
if ( ! is_front_page() ) {
return;
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'LocalBusiness',
'name' => get_bloginfo( 'name' ),
'image' => get_site_icon_url(),
'url' => home_url( '/' ),
'telephone' => '+1-555-010-0100',
'address' => [
'@type' => 'PostalAddress',
'streetAddress' => '123 Main St',
'addressLocality' => 'Springfield',
'addressRegion' => 'IL',
'postalCode' => '62701',
'addressCountry' => 'US',
],
'openingHoursSpecification' => [
'@type' => 'OpeningHoursSpecification',
'dayOfWeek' => [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday' ],
'opens' => '09:00',
'closes' => '17:00',
],
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>' . "\n";
} );
Replace the placeholder name, phone number, and address with your actual business details pulled from options or post meta rather than hardcoding them directly, if you're building this for a client site or a business that might change locations.
HowTo Schema Example
For step-by-step tutorial content, HowTo schema can make individual steps eligible to appear directly in search results:
add_action( 'wp_head', function () {
if ( ! is_single( 'how-to-clear-your-browser-cache' ) ) {
return;
}
$steps = [
[ 'name' => 'Open browser settings', 'text' => 'Click the three-dot menu and select Settings.' ],
[ 'name' => 'Find privacy options', 'text' => 'Navigate to Privacy and Security.' ],
[ 'name' => 'Clear browsing data', 'text' => 'Select Clear browsing data and choose Cached images and files.' ],
];
$step_items = array_map( function ( $step ) {
return [
'@type' => 'HowToStep',
'name' => $step['name'],
'text' => $step['text'],
];
}, $steps );
$schema = [
'@context' => 'https://schema.org',
'@type' => 'HowTo',
'name' => get_the_title(),
'step' => $step_items,
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema ) . '</script>' . "\n";
} );
As with FAQ schema, Google has also narrowed HowTo rich result eligibility over time, mostly limiting the rich visual treatment to desktop results. The underlying markup is still worth including since it accurately describes your content structure to any consumer of structured data, not just Google's current rich-result rules.
Avoiding Duplicate or Conflicting Schema
A common mistake once you start combining manual code with a plugin is ending up with the same schema type output twice for the same page — for instance, both Yoast's automatic Article schema and a custom wp_head snippet adding another Article block. Search engines don't necessarily reject this outright, but it creates ambiguity about which version is authoritative, and it's messier to maintain.
Before adding custom schema, check your plugin's output first. View source on a live page, search for application/ld+json, and read through what's already there. If Yoast or Rank Math already outputs the type you need, extend or configure it through the plugin's filters or settings rather than duplicating it — both plugins expose filters (wpseo_schema_graph for Yoast, rank_math/json_ld for Rank Math) specifically so you can modify their generated schema instead of adding a second, competing block.
Method 2: Use an SEO Plugin
If writing PHP for every schema type isn't appealing, both major SEO plugins generate schema automatically:
- Yoast SEO builds a connected schema graph (Organization/Person, WebSite, WebPage, Article) automatically for every page, and lets you fill in additional details like your organization's logo and social profiles under Yoast SEO > Settings > Site Representation.
- Rank Math includes a Schema Generator under each post's Rank Math meta box, with ready-made templates for Article, Product, Recipe, Event, FAQ, How-To, and more — you fill in a form and it outputs the JSON-LD for you.
Plugin-generated schema is a good default for most sites, and it's worth using instead of hand-rolled schema if you don't have a specific reason to need custom fields the plugin doesn't expose. If you do need something custom (a niche schema type, or fields pulled from custom post meta), the manual wp_head approach above still works alongside a plugin, as long as you don't end up outputting the same schema type twice on the same page.
Method 3: WooCommerce Product Schema
If you're running a store, WooCommerce outputs Product schema automatically for product pages via structured data functions built into WooCommerce core, covering price, availability, and (once reviews accumulate) aggregate rating. Most WooCommerce-focused SEO plugins extend this further, but the baseline is already present without any extra configuration.
Validating Your Schema
Never assume markup is correct just because it renders — always test it:
- Google Rich Results Test — paste a URL or raw code and see exactly which rich result types Google recognizes, plus any errors or warnings.
- Schema.org Validator — validates against the full Schema.org vocabulary, useful for types Google's tool doesn't specifically test for.
- View page source (
Ctrl+U/Cmd+Option+U) and search forapplication/ld+jsonto confirm your script tag is actually rendering with the expected values, not an empty or malformed object.
Fix any errors flagged, but don't stress over "warnings" for optional fields you don't have — required-field errors are what actually block a rich result from being eligible.
Frequently Asked Questions (FAQ) About Schema Markup in WordPress
No. Schema markup makes your content eligible for a rich result, but Google's algorithms still decide whether, when, and how to display one. Accurate, error-free schema improves your odds; it isn't a guarantee.
Google explicitly recommends JSON-LD, and it's easier to implement in WordPress since it's a self-contained script block that doesn't require modifying your theme's visible HTML structure. Microdata still works but requires embedding attributes directly into your markup, which is more fragile to maintain.
Yes, as long as you avoid outputting the same schema type for the same entity twice on one page, which can create duplicate or conflicting data. If you add custom schema manually, check whether your SEO plugin already covers that type before adding your own.
Use an SEO plugin's built-in schema generator. Rank Math's Schema Generator and Yoast's automatic schema graph both cover the most common types (Article, Product, FAQ, How-To, Review) through a settings form rather than PHP.
Run the page through Google's Rich Results Test and the Schema.org Validator. Both will parse your JSON-LD and flag missing required fields or structural errors, and the Rich Results Test specifically tells you which Google rich result types your page currently qualifies for.
In 2023, Google significantly restricted which sites are eligible for the FAQ rich result visual treatment, largely limiting it to established government and health-related sites regardless of markup correctness. The schema itself is still valid and can still be used by other tools, but the visual rich result specifically may simply no longer be available to most sites.
Yes, WooCommerce outputs Product schema automatically on product pages, covering fields like price, currency, and availability, with aggregate rating added once a product has reviews. No extra plugin is required for this baseline, though SEO plugins can extend it further.
No meaningfully. A JSON-LD script block is typically a few hundred bytes to a couple of kilobytes of text, which is negligible compared to images, fonts, and JavaScript bundles. If you're focused on site speed generally, schema markup isn't where the gains are — see how to speed up your WordPress site for what actually moves the needle.
Conclusion
Schema markup won't change what your content says, but it changes how confidently search engines can describe it, which is exactly what unlocks rich results, better click-through rates, and clearer signals for the AI-driven search features that increasingly rely on structured data rather than guessing from plain text. JSON-LD via wp_head gives you full control when you need it, and a plugin's built-in schema generator covers most sites without writing a line of PHP.
Whichever route you take, always validate the output with Google's Rich Results Test before assuming it's correct — schema that "looks right" in your code editor can still fail validation over a missing required field or a subtly wrong data type.


