---
title: "WooCommerce schema markup: what your store outputs"
url: https://wpministry.co/blog/woocommerce-schema-markup-rich-snippets/
description: "WooCommerce prints product structured data itself. Test a page in the Rich Results Test, fix any error, then fill gaps such as a GTIN, a brand or shipping."
lang: en-US
---

Skip to content

# WooCommerce schema markup: what your store outputs and how to fill the gaps

WooCommerce prints product structured data on its own, with the name, price, stock status and rating. Test a product page with Google's Rich Results Test, fix any error, then add what is missing, such as a GTIN, a brand, and shipping and return details. Markup does not guarantee a rich result.

By

WP Ministry

Published

October 7, 2026

## In short

- WooCommerce adds Product markup to a product page that has a price or a rated review. No plugin is needed for that.
- An error in Google's Rich Results Test makes the item invalid. A warning leaves it valid.
- The usual gaps are a GTIN, a brand, reviews, and shipping and return details.
- Fill a gap in WooCommerce's own fields first, then with a plugin, then with the woocommerce_structured_data_product filter.
- Mark up only what a visitor can see on the page. Never add a rating the page does not show.
- Markup makes a page eligible for a rich result. Google does not guarantee one.

WooCommerce already puts structured data on a product page. It is a block of JSON-LD that names the product as a schema.org `Product` and gives its price, its stock status and, when customers have rated it, its rating. That is what Google reads to show a product rich result, so the work is to check what your store prints and to fill in what is missing.

Markup makes a page eligible for a rich result. Google says plainly that it does not guarantee one, and its documentation for product markup promises nothing about where the page ranks.

## What a product rich result is

A rich result is a search result that shows more than a title and a description. For products, Google's documentation describes two kinds.

- **A product snippet** is a text result with extra product information, such as ratings, review information, price and availability.
- **A merchant listing** is for a page where a shopper can buy the product. It can show price, availability, and shipping and return information, in places such as the shopping knowledge panel, Google Images and popular product results.

A store's product page can qualify for both. Google notes that a page with the properties a merchant listing requires can be eligible for product snippets too. Only a page about one product, or the variants of one product, qualifies. A category page does not.

| | Product snippet | Merchant listing |
| --- | --- | --- |
| Required | `name`, and one of `review`, `aggregateRating` or `offers` | `name`, `image` and `offers`, with a `price` and a `priceCurrency` in the offer |
| Recommended, among others | `aggregateRating`, `review`, and in the offer `availability`, `priceCurrency` and `priceValidUntil` | `brand.name`, a GTIN, `mpn`, `sku`, `description`, `aggregateRating`, `review`, and in the offer `availability`, `shippingDetails`, `hasMerchantReturnPolicy` and `itemCondition` |

## What WooCommerce outputs on its own

WooCommerce builds the markup while it draws the product page and prints it near the end of the page, in the footer. To see it, open a product page, view the page source, and search for `application/ld+json`.

html

```
<script type="application/ld+json">{"@context":"https://schema.org/","@type":"Product", ... }</script>
```

The block is part of the HTML the server sends, which is where Google recommends a store put its product markup. On some pages the `Product` sits inside an `@graph` list beside a `BreadcrumbList`.

This is what WooCommerce 11.1 puts in the `Product`, and where each value comes from.

| Property | Comes from | Left out when |
| --- | --- | --- |
| `name`, `url` | The product's name and address | Never |
| `description` | "Product short description", or the main description if that is empty | Never, though it can be empty |
| `image` | The product image | The product has no image |
| `sku` | The SKU field. If it is empty, WooCommerce uses the product's ID number | Never |
| `gtin` | The "GTIN, UPC, EAN, or ISBN" field on the Inventory tab | The value is not 8, 12, 13 or 14 digits long |
| `brand` | The brand assigned to the product. With more than one, the first by name | No brand is assigned |
| `offers` | The price, the store's currency and the stock status. WooCommerce adds `priceValidUntil` and names the store as `seller` | The product has no price |
| `aggregateRating`, `review` | The average rating, the number of reviews, and the five most recent rated reviews | No customer has rated the product, or reviews or star ratings are switched off |

- **A product with no price and no rated review gets no `Product` block at all.**
- **A variable product whose variations differ in price** gets an `AggregateOffer` with the lowest and highest price.
- **WooCommerce's product markup has no `mpn`, shipping details, return policy or item condition.**

If a product page has a price and still shows no `Product` block, look at its template. WooCommerce gathers the data on an action its own product template runs, `woocommerce_single_product_summary`. A custom template that never runs it leaves WooCommerce nothing to print.

## Test one product page

Pick a product that has a price, an image and at least one review, so that every part of the markup is present.

1. ### Step 1: Run the page through the Rich Results Test
   Open Google's Rich Results Test (https://search.google.com/test/rich-results) and enter the full address of the product page. The page has to be reachable without logging in. To check a change before it is live, choose "Code" in place of "URL" and paste the markup.
2. ### Step 2: Read which results were found
   The test lists each kind of rich result it found on the page, and the items under each. For a store you should see product snippets and merchant listings. A status such as "N valid item(s) detected" means nothing blocks the item.
3. ### Step 3: Fix errors first
   An item with an error is invalid. Search Console calls this a critical issue, and an invalid item cannot appear as a rich result. Open the item to see which property is at fault. With WooCommerce's own markup, look at the product before the code: a merchant listing requires an image, for example, and WooCommerce leaves `image` out when the product has none.
4. ### Step 4: Then weigh the warnings
   An item with warnings is still valid. A warning names a recommended property that is absent. Google's advice is that a few complete and accurate recommended properties are worth more than every possible one.
5. ### Step 5: Compare the values with the page
   Check that the price, the stock status and the rating in the markup are the ones a visitor sees. Google requires structured data to be a true representation of the page.

Once Google has crawled the store, Search Console shows two reports under Shopping, one for product snippets and one for merchant listings. Each has a table named "Why items are invalid" and one named "Improve item appearance". A report appears only after Google has found valid markup of that kind, and it shows a sample of items, not every one.

## Fill in what is missing

Work in this order: a field WooCommerce already has, then a plugin, then code.

### A GTIN, a brand and a part number

- **GTIN.** Edit the product, open the Inventory tab and fill in "GTIN, UPC, EAN, or ISBN" with the number from the barcode. WooCommerce strips everything but digits and adds the value to the markup only if 8, 12, 13 or 14 digits remain.
- **Brand.** Go to Products > Brands to create the brand, then choose it in the "Product brands" panel when you edit the product. Brands are part of WooCommerce itself. Google asks for at most one brand name, and where a product has more than one, WooCommerce's markup takes the one whose name comes first in the alphabet.
- **Part number.** WooCommerce has no field that feeds `mpn`. A plugin or the filter below can add it.

If a product has no GTIN, leave the field empty. Do not invent a number.

### Ratings and reviews

Rating markup appears when three things are true: "Enable product reviews" and "Enable star rating on reviews" are both selected under WooCommerce > Settings > Products, and at least one customer has rated the product.

A product with a price and no reviews is still eligible for a product snippet, because Google requires only one of `review`, `aggregateRating` and `offers`. Its documentation says the test may show a warning in that case. The answer is real reviews from customers, never a rating written into the markup.

### Shipping and returns

Google recommends stating these once for the whole business and not on every product. There are three ways to do it.

- **Settings in Search Console.** Under Settings > Shopping > Shipping and returns you can enter delivery times, shipping costs, the return window and return costs. Google approves shipping details automatically and verifies a return policy by hand, so a new return policy waits as pending first.
- **Google Merchant Center,** if the store has an account.
- **Markup on one page.** Put the shipping and return policy under `Organization` markup on the page that describes your policy. Google says it does not need to be on every page.

Where more than one is present, Google puts settings in Merchant Center or Search Console ahead of markup on the site. Use `shippingDetails` or `hasMerchantReturnPolicy` on a single offer only for a product whose terms differ from the store's standard ones.

### What an SEO plugin adds

Some SEO plugins build on WooCommerce's markup instead of writing a second set. Yoast's add-on for stores, Yoast WooCommerce SEO, is one example. Its documentation says it alters the `Product` piece, applies its own validation to each value WooCommerce supplies, sets `brand` and `manufacturer` from taxonomies you choose, and stitches the offers, reviews and rating into the graph Yoast SEO outputs. It also adds fields for product identifiers on a Yoast SEO tab in the product data box.

Before you install a plugin for this, read its documentation for two things: which properties it adds, and whether it works with WooCommerce's markup or beside it. WordPress vs Shopify for SEO (https://wpministry.co/blog/wordpress-vs-shopify-seo/) covers what an SEO plugin does for the rest of a store.

## Add a property with code

WooCommerce passes the finished `Product` array through a filter, `woocommerce_structured_data_product`, with the product as the second argument. Its own Brands feature uses the same filter to add `brand`.

This example adds `mpn`. It reads the value from a product attribute named MPN, so add that attribute to the product first and select "Visible on the product page". The part number is then on the page for visitors as well as in the markup.

functions.php

```
add_filter( 'woocommerce_structured_data_product', 'example_add_mpn_to_product_markup', 10, 2 );

function example_add_mpn_to_product_markup( $markup, $product ) {
	if ( ! $product instanceof WC_Product ) {
		return $markup;
	}

	$mpn = $product->get_attribute( 'MPN' );

	if ( '' !== $mpn && empty( $markup['mpn'] ) ) {
		$markup['mpn'] = $mpn;
	}

	return $markup;
}
```

Goes in a child theme's functions.php, or in a small plugin of your own.

Give the attribute one value. If it holds several, WooCommerce returns them joined by commas, and that is not a part number.

Put the code in a child theme so that a theme update does not remove it. How to create a WordPress child theme (https://wpministry.co/blog/create-wordpress-child-theme/) shows how to make one. A mistake in `functions.php` can stop the whole site from loading, so make the change on a staging copy (https://wpministry.co/blog/set-up-wordpress-staging-site/) first, or be ready to remove the lines over SFTP. If you would rather not edit code, our WooCommerce maintenance service (https://wpministry.co/services/woocommerce-maintenance/) is a care plan for a store that includes 5 hours a month of fixes and edits.

To check it, clear any page cache, reload the product page, and look for `"mpn"` in the JSON-LD block. Then run the page through the test again. To undo it, delete the lines.

## Two sets of markup for one product

A second plugin or the theme can write its own `Product` block beside WooCommerce's. A reviews plugin or a schema plugin may do this. The two blocks then describe one product twice, and they can disagree: one has the price, the other has the rating, or each has a different price.

To find it, search the page source for `"@type":"Product"` and count the matches, allowing for spaces around the colon. The Rich Results Test shows the same thing as two items under one result type, each with its own errors and warnings.

To fix it, keep one set.

- **Look for a setting first.** If one of the two plugins has a switch for its product markup, turn off the block that is less complete.
- **Do not strip WooCommerce's markup from under a plugin that builds on it.** A plugin of that kind, as Yoast WooCommerce SEO describes itself, works from those values.
- **If the other plugin writes a complete block of its own and has no switch,** you can stop WooCommerce printing its `Product` with the filter that decides which types go on a page.

functions.php

```
add_filter( 'woocommerce_structured_data_type_for_page', 'example_remove_wc_product_markup' );

function example_remove_wc_product_markup( $types ) {
	return array_diff( $types, array( 'product' ) );
}
```

Removes WooCommerce's own Product markup. Its breadcrumb markup stays.

Run the test afterwards. It should show one product item, with the price and rating a visitor sees.

## What structured data cannot do

- **It does not guarantee a rich result.** Google's guidelines say structured data enables a feature and does not guarantee that it appears.
- **It is not a way to move up the results.** Google's product documentation describes what a result can show and says nothing about position. Where its guidelines do mention ranking, it is to say that a manual action for structured data costs a page its rich results and does not affect how it ranks.
- **It cannot describe what the page does not show.** Google's rule is not to mark up content that is not visible to readers. A rating in the markup has to be on the page, with the reviews behind it.
- **It cannot borrow reviews.** Google says not to aggregate reviews or ratings from other websites, and not to include fake reviews or ones written for a reward that is not disclosed. Breaking these rules can bring a manual action.
- **It does not fix a slow or broken product page.** How to speed up a WooCommerce store (https://wpministry.co/blog/speed-up-woocommerce/) covers the first of those.

## Common questions

Does schema markup improve my rankings?

Google's documentation for product markup does not say so. It describes markup as a way to make a page eligible for a richer result, and it does not guarantee that result. Treat markup as a way to describe your products accurately, not as a ranking method.

The test warns that my product has no review or rating. Is that a problem?

No. A product snippet needs only one of a review, a rating or an offer, and a product with a price has an offer. The warning goes away when customers leave rated reviews. Do not add a rating by hand to clear it.

Can reviews on my own product pages earn stars?

They can be marked up, and WooCommerce does it for you. Google's rule against reviews that a business controls about itself applies to `LocalBusiness` and `Organization` markup, which is a rating of the business. A customer's review of a product is a different thing. Stars remain Google's decision either way.

Do I need Google Merchant Center as well?

Not for the markup to work. Google says that providing both structured data on your pages and a Merchant Center feed gives a store the most ways to be eligible, and that some results combine the two. A store can start with markup alone.

## More on this subject

- WooCommerce stores: https://wpministry.co/topics/woocommerce/
- Error fix WooCommerce add to cart not working: find the cause and fix it: https://wpministry.co/errors/woocommerce-add-to-cart-not-working/
- Resource WooCommerce checkout is down: a runbook: https://wpministry.co/resources/woocommerce-checkout-down-runbook/
- Cost guide WooCommerce maintenance cost: why a store costs more, and what the extra should buy: https://wpministry.co/cost/woocommerce-maintenance/
- Error fix WooCommerce order stuck on "Pending payment": how to tell paid from unpaid, and fix the cause: https://wpministry.co/errors/woocommerce-orders-stuck-pending-payment/
- Resource WooCommerce sale readiness checklist: Black Friday or any big sale: https://wpministry.co/resources/woocommerce-sale-readiness-checklist/
- Glossary term Action Scheduler: https://wpministry.co/glossary/action-scheduler/

## Not sure what is wrong?

Tell us what you see. We reply with the cause and a fixed quote, and the diagnosis is free.

Get a free diagnosis: https://wpministry.co/free-diagnosis/

May we measure how this site is used?

A yes notes the pages of this visit and loads Google Analytics. A no keeps and loads nothing. See the cookies page (https://wpministry.co/legal/cookies/).

If you say yes, we note the pages you look at during this visit and load Google Analytics. If you say no, nothing is kept and nothing is loaded. The site works the same either way. The cookies page (https://wpministry.co/legal/cookies/) has the details.

## Structured data

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://wpministry.co/#organization",
      "name": "WP Ministry",
      "url": "https://wpministry.co/",
      "logo": "https://wpministry.co/brand/logo.png",
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "1309 Coffeen Ave STE 1200",
        "addressLocality": "Sheridan",
        "addressRegion": "WY",
        "postalCode": "82801",
        "addressCountry": "US"
      },
      "sameAs": [
        "https://www.linkedin.com/company/wp-ministry/",
        "https://x.com/wpministryco",
        "https://www.instagram.com/wpministryco/",
        "https://www.facebook.com/wpministryco",
        "https://www.tiktok.com/@wpministryco",
        "https://www.pinterest.com/wpministryco/",
        "https://www.youtube.com/channel/UCOZRuAukk5QSTRPG6BaHl3Q"
      ]
    },
    {
      "@type": "WebSite",
      "@id": "https://wpministry.co/#website",
      "name": "WP Ministry",
      "url": "https://wpministry.co/",
      "inLanguage": "en-US",
      "publisher": {
        "@id": "https://wpministry.co/#organization"
      }
    },
    {
      "@type": "Article",
      "@id": "https://wpministry.co/blog/woocommerce-schema-markup-rich-snippets/#article",
      "headline": "WooCommerce schema markup: what your store outputs and how to fill the gaps",
      "description": "WooCommerce prints product structured data itself. Test a page in the Rich Results Test, fix any error, then fill gaps such as a GTIN, a brand or shipping.",
      "url": "https://wpministry.co/blog/woocommerce-schema-markup-rich-snippets/",
      "mainEntityOfPage": "https://wpministry.co/blog/woocommerce-schema-markup-rich-snippets/",
      "inLanguage": "en-US",
      "image": "https://wpministry.co/og/blog/woocommerce-schema-markup-rich-snippets.png",
      "author": {
        "@id": "https://wpministry.co/#organization"
      },
      "publisher": {
        "@id": "https://wpministry.co/#organization"
      },
      "datePublished": "2026-10-07T00:00:00.000Z",
      "dateModified": "2026-10-07T00:00:00.000Z"
    },
    {
      "@type": "BreadcrumbList",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "Home",
          "item": "https://wpministry.co/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Guides",
          "item": "https://wpministry.co/blog/"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "WooCommerce schema markup: what your store outputs and how to fill the gaps"
        }
      ]
    }
  ]
}
```