// blog / wordpress setup

How I add schema to WordPress so it validates and matches the page

To add schema to WordPress, I write it as JSON-LD, either through the SEO plugin's schema settings or as one small block printed on wp_head, and join the pieces with @id so they describe one business. Every type has to match what's visible on the page, and I never mark up reviews the page doesn't show.

By Muhammad Younus Updated 10 min read WordPress setup

WordPress SEO setup: illustration for "Adding schema to WordPress"
// 01 - the basics

What is schema markup, and is it still relevant?

Schema markup is structured data written in the schema.org vocabulary. Google calls structured data a standardized format for providing information about a page and classifying its content, and it uses what it finds to understand the page. On WordPress that's a JSON-LD block saying who publishes the site, what the page is and, for a business, where it is. Most of what I do as a schema markup consultant is making that block and the visible page say the same thing.

Is it still relevant? Yes, for narrower reasons than many plugin pages suggest. Google says it doesn't guarantee structured data will show up in results, even when the Rich Results Test passes it, and some features have gone: its changelog says the FAQ rich result stopped appearing in Search from 7 May 2026. Breadcrumbs, product snippets, merchant listings and review snippets are still in Google's list of supported features, and they all need the markup.

// 02 - the plugins

What is the best schema plugin for WordPress?

Usually the one that's already installed. Most WordPress sites run Yoast or Rank Math, and both print structured data before anyone touches a setting. Yoast builds one @graph of pieces joined by @id, and its documentation says every piece can be altered, extended or switched off through its API. Rank Math adds schema through a Schema Generator in the post editor. Here's which SEO plugin I'd pick.

A dedicated schema plugin earns its place when the SEO plugin can't express a type. Schema & Structured Data for WP & AMP lists more than 35 schema types on its plugin page. Schema Pro maps page fields such as title, author and date into markup, and says it works alongside Yoast, with settings that decide which plugin controls what. That setting is the whole question: if both plugins print an Organization, the page describes the business twice, so I switch the duplicate off first.

Where the schema comes from, and when
What the site needsWhat I useWhy
Organization or Person, WebSite, WebPage, breadcrumbs and articlesThe SEO plugin's own settingsIt already prints these, linked, on every template
A specific business type with address, hours and area servedThe plugin's local settings, or its filter when the type isn't offeredThe business stays one entity inside the existing graph
A type the plugin can't express at allOne JSON-LD block from a child theme or site plugin, reusing the same @idIt survives a theme update and doesn't describe the business twice
A dedicated schema plugin on top of the SEO pluginOnly for types the SEO plugin can't printTwo plugins tend to mean two graphs for one business

WooCommerce is its own case. It prints Product markup on product pages with no SEO plugin involved: name, URL, description, image, SKU, a GTIN when a valid one's entered, and an Offer with price, currency, availability and a priceValidUntil date. Ratings and the five most recent reviews appear only when the product has them. It doesn't print a brand, shipping details or a return policy; those go in through the woocommerce_structured_data_product filter. For stores already seeing warnings, here's how I fix Product snippet errors without fake reviews.

Adding schema in Elementor follows the same rule. The builder lays out the visible page and the SEO plugin prints the graph, so I don't paste JSON-LD into an HTML widget on each page, where nobody finds it later.

// 03 - the types

Which schema types does a business site need?

Fewer than most plugins offer. A business needs its identity said once, then each page described as what it is.

The types a WordPress business site usually needs
TypeWhat it tells GoogleWhere it goes
Organization, or Person for a one-person brandWho publishes the site: name, logo and profilesOnce, sitewide, with one @id
WebSiteThe site itself, published by that entityOnce, sitewide
WebPage and BreadcrumbListWhat this URL is and where it sitsEvery page, matching the visible breadcrumb
A LocalBusiness subtypeThe business type, address, hours and area servedWhere those facts are shown, usually home and contact
Article or BlogPostingWho wrote a post and whenPosts only

Google's local business documentation asks for the most specific subtype possible, so a fireplace company isn't just LocalBusiness and a jeweller isn't just a Store. On GK Jewelers' store, the homepage declares a JewelryStore with its postal address, plus WebSite and SearchAction markup.

Alpenglow Fireplace, a fireplace and chimney site, went live with HVACBusiness markup declaring its postal address, geo coordinates, opening hours and the city it serves. That shows what was built, not that the markup caused anything in Search Console. Schema is one layer of the website side of local search, and it only helps when the pages and the Business Profile say the same thing.

Rather have me do this? One person, whole job.

Book a call on Upwork
// 04 - the method

How I add schema to a WordPress site, step by step

Six moves. Old sites can carry markup from plugins they stopped using years ago, and some themes print their own, so I read what's there first, even on a fresh install.

  1. Step 01

    Read what the page prints today

    I run the homepage, a service page and a post through the Rich Results Test and read the source for every application/ld+json block, noting which plugin printed each one. If two blocks already describe the business, that gets fixed first.

  2. Step 02

    Copy the facts from the page, not from memory

    Name, logo, address, phone, hours and towns served get written down exactly as the page and the Business Profile show them. If those two disagree, the markup waits. Each fact then goes on the page that shows it.

  3. Step 03

    Pick the most specific type

    A fireplace service became HVACBusiness, not LocalBusiness. If nothing fits closely, I stay with the nearest honest parent type rather than stretch one that doesn't.

  4. Step 04

    Set the entity once in the SEO plugin

    In Yoast that's the Organization or Person setting. In Rank Math it's Titles and Meta, Local SEO, once the Local SEO module's switched on. Every template then inherits the same entity.

  5. Step 05

    Give every piece a stable @id

    The business gets one identifier, like https://example.com/#organization, and every other piece points at it. WebSite names it as the publisher, and WebPage says it's part of the WebSite. That turns a pile of blocks into one described business.

  6. Step 06

    Add only what the plugin can't

    A missing property goes in through the plugin's filter. A missing type goes in as one JSON-LD block that reuses the same @id, printed the way the next section shows.

// watch out

Don't mark up what the page doesn't show. Google's general guidelines say not to mark up content that isn't visible to readers of the page, so hours in the markup but missing from the page break the rule quietly, even when they're right.

// 05 - no plugin

How do I add schema to WordPress without a plugin?

Put the code where theme updates can't reach it: a child theme's functions.php, a small site plugin, or a code snippets plugin such as WPCode if you'd prefer not to touch files. WordPress's theme handbook warns that a function added to the parent theme's functions.php will disappear the next time the theme is updated. Here's the pattern I use on a contact page.

  1. Step 01

    Hook into wp_head

    WordPress's wp_head hook prints scripts or data in the head tag on the front end, which is where the block belongs: add_action( 'wp_head', 'site_contact_schema' );

  2. Step 02

    Print it only where the facts are shown

    The function returns early unless is_page( 'contact' ) is true, so the hours and address are only claimed on the page that shows them.

  3. Step 03

    Build the data as a PHP array

    $data = array( '@context' => 'https://schema.org', '@type' => 'HVACBusiness', '@id' => 'https://example.com/#organization', 'name' => 'Example Fireplace', 'openingHours' => 'Mo-Fr 08:00-17:00', 'areaServed' => 'Calgary' ); The @id is the one the SEO plugin already uses, so this adds facts to that business rather than creating a second one.

  4. Step 04

    Encode it and print it

    echo '<script type="application/ld+json">' . wp_json_encode( $data ) . '</script>'; I let WordPress's own wp_json_encode() write the JSON rather than typing it by hand, so a quote mark in a business name can't break the block.

  5. Step 05

    Pull page facts from fields, not the code

    When each location's hours live in Advanced Custom Fields, the array reads them with get_field(), which returns the value of a specific field. The markup then changes whenever someone edits the field, and nobody has to find the code.

// pro tip

Check the live page source afterwards for exactly one block with that @id. Two blocks naming the same business differently is the mess I'm most often asked to untangle.

// 06 - testing

How do I test it before and after publishing?

Two tools check two different things, and a pass in one says nothing about the other. After launch, Search Console tells me whether it's holding up.

  1. i.

    Rich Results Test, on the code

    Before anything ships I paste the snippet into the Rich Results Test, which accepts code as well as a URL and flags missing required properties.

  2. ii.

    Schema Markup Validator, for the vocabulary

    It checks for valid schema.org properties, including types Google doesn't consume, so it catches a misspelt property on HVACBusiness that the Rich Results Test ignores.

  3. iii.

    Rich Results Test, on the live URL

    After publishing I test the real URL, because the rendered page is what Google reads, and a caching plugin or theme update can change the output.

  4. iv.

    URL Inspection in Search Console

    Google points to URL Inspection to confirm it's actually discovered the markup on a page, which is a different thing from the markup existing.

  5. v.

    Rich result reports over the next weeks

    Google warns that markup can break after deployment through templating or serving issues. The rich result reports show errors across every URL of a type.

// pro tip

A clean test means a page is eligible, not that anything will show, so I never sell markup as a listing upgrade.

// 07 - reviews

Why won't I add review stars that aren't earned?

Because a business can't award them to itself. Google's review snippet guidelines say that when an entity controls the reviews about itself, its pages using LocalBusiness or any other Organization type aren't eligible for star reviews. Testimonials a business chose and published on its own site are exactly that.

Marked-up reviews have to be readily available on the marked-up page. An AggregateRating copied from a Business Profile into the homepage code fails both tests.

It isn't a harmless gamble. Google's structured data guidelines say violations can lead to a manual action that takes pages out of the running for rich results, which throws away the only thing the markup was for. Where reviews are real, visible and about something other than the business, like a product a store sells, I'll mark them up exactly as the page shows them.

// when to hand this over

If you'd like one person to read what your pages print, set the entity once and test it on the live URLs, that's the schema work I do, remotely. Here's how a project runs from the first call.

Book a call on Upwork

Is this your site? Send me the URL.

Book a call on Upwork
// questions - wordpress setup

What else do people ask about this?

How do I insert a schema in WordPress?

Through the SEO plugin's schema settings for the common types, or as one JSON-LD block printed on wp_head from a child theme or site plugin for anything it can't express. Either way it's one graph, joined by @id, on the page it describes.

Is JSON-LD better than microdata on WordPress?

For WordPress, yes. Google supports JSON-LD, microdata and RDFa and recommends JSON-LD as the easiest to maintain, and a theme change doesn't take it with it.

Will adding schema improve my rankings?

I don't promise that. Google doesn't guarantee structured data will show in results even when it's correct. What markup does is state the facts plainly, and that's the part I can test.

Can I mark up the reviews from my Google Business Profile?

Not in your own site's business markup. Google treats reviews a business controls about itself as ineligible for stars on LocalBusiness or Organization pages, and marked-up reviews have to be visible on that page.

Does WooCommerce add product schema on its own?

Yes. It prints Product and Offer markup on product pages, with ratings only when the product has them. It doesn't add a brand, shipping details or a return policy, so those need its filter or an SEO plugin that adds them.

// sources - checked

Where do these rules come from?

Platform and Google documentation, opened and read on the day this was written. Everything else is how I work.

  1. Google Search Central: introduction to structured data markup
  2. Google Search Central: general structured data guidelines
  3. Google Search Central: review snippet structured data
  4. Google Search Central: local business structured data
  5. Search Console Help: Rich Results Test
  6. Google Search Central Blog: an update on the Structured Data Testing Tool
  7. Yoast developer portal: schema functional specification
  8. Rank Math knowledge base: rich snippets and the Schema Generator
  9. Google Search Central: structured data markup that Google Search supports
  10. Google Search Central: latest documentation updates (FAQ rich result deprecation, May 2026)
  11. WordPress.org: Schema & Structured Data for WP & AMP plugin
  12. Schema Pro (Brainstorm Force): WordPress schema plugin
  13. WooCommerce source: class-wc-structured-data.php
  14. WordPress Theme Handbook: child themes
  15. WordPress Code Reference: the wp_head hook
  16. WordPress.org: WPCode, insert headers and footers and custom code snippets
  17. Advanced Custom Fields: get_field()
Muhammad Younus, WordPress technical SEO consultant
// who wrote this

One consultant, writing down his own method

I'm Muhammad Younus, a WordPress technical SEO consultant with 8+ years of experience and 100+ client sites fixed. Top Rated Plus on Upwork with a 100% Job Success Score. Every post here is the way I actually do the job, written in the first person because it's one person doing it, remotely, with no team behind the name.

Nothing in these posts is a client I can't name or a number I can't show. Where a figure comes from a client's Search Console, it's from a published case study, and where a rule comes from Google or a platform, the source is linked at the bottom of the post.

// the service behind this post
Muhammad Younus laughing, a wide genuine smile
// next step

Want this done on your own site?

Book a call on Upwork, send the URL and read-only Search Console access, and you'll get a written scope for the search side before any work starts. One person does the whole job, remotely.

Book a call on Upwork