Product schema is the most commercially valuable structured data you can add to an online store, and also the one people most often implement incompletely. The gap between a bare Product block and a complete one is the difference between a plain result and a listing showing price, availability, review stars, shipping, and returns, the details shoppers actually compare.
This guide covers the full picture: the two product rich results and why the distinction matters, the required and recommended fields, variants, the shipping and return markup most stores skip, how WooCommerce fits in, and how to validate the result.
One framing to keep in mind throughout: structured data isn't a ranking signal, and no plugin can promise a rich result. What Product markup does is make a page eligible for richer listings and give shoppers more reason to click. Google decides what to display, and eligibility depends on getting the details right. That's the lens this guide takes, completeness and accuracy, not shortcuts.
Two rich results, not one
The single most useful thing to understand before writing any markup: Google has two product features, and they have different requirements.
Product snippets are the enhanced organic result, review stars, ratings, price, and basic availability attached to your normal listing. This is the baseline.
Merchant listings are for pages where customers can actually purchase, and they carry more detail: shipping, returns, and apparel sizing among them. They power the richer shopping experiences.
The two overlap, and that overlap works in your favour. Per Google's documentation, adding the required properties for merchant listings generally makes your pages eligible for product snippets too. Each feature has its own enhancements, so the more valid properties you add, the more surfaces you qualify for. The practical strategy is to aim for the merchant listing and get the snippet as a byproduct. For the full picture of which schema types support which rich results, see our rich results overview.
The required fields
Every Product intended for a rich result needs a core set. Here's a complete, valid example:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "TrailBlaze Running Shoes",
"image": [
"https://example.com/photos/trailblaze-1x1.jpg",
"https://example.com/photos/trailblaze-4x3.jpg",
"https://example.com/photos/trailblaze-16x9.jpg"
],
"description": "Lightweight trail running shoes with responsive cushioning.",
"sku": "TB-2200",
"brand": {
"@type": "Brand",
"name": "TrailBlaze"
},
"offers": {
"@type": "Offer",
"url": "https://example.com/products/trailblaze",
"priceCurrency": "USD",
"price": "129.00",
"availability": "https://schema.org/InStock",
"itemCondition": "https://schema.org/NewCondition"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.6",
"reviewCount": "218"
}
}The non-negotiables:
nameandimage, provide several images in different aspect ratios where you can; Google uses them across different layouts.offerswithpriceandpriceCurrency, and the price must match the visible page exactly. A mismatch is the fastest way to lose the result.availability, reflect real stock with the schema.org values (InStock,OutOfStock,PreOrder,BackOrder).
For review stars, aggregateRating or review must correspond to reviews genuinely displayed on the page. Marking up ratings that aren't visible is a policy violation, not a shortcut.
Product identifiers
Identifiers help Google connect your listing to the exact product across the web, which matters for shopping surfaces.
Include a global identifier wherever you have one: gtin (the barcode number in its various lengths), mpn (manufacturer part number), and brand. For products genuinely without a GTIN, handmade or custom goods, you can omit it, but include whatever identifiers do apply. The rule of thumb: the more precisely you identify the product, the more confidently Google can match and display it. For a broader look at how identifiers and typing work across schema.org, see our schema types directory.
Variants: don't flatten them
Many products come in variations, sizes, colours, materials, configurations. Google added dedicated variant support, and both product snippets and merchant listings support it. Modelling variants properly is one of the highest-value moves for a catalogue that has them.
The pattern uses ProductGroup as the parent, with individual Product entries as variants connected by hasVariant, and variesBy declaring which attributes differ:
{
"@context": "https://schema.org",
"@type": "ProductGroup",
"name": "TrailBlaze Running Shoes",
"productGroupID": "TB-2200-GROUP",
"variesBy": [
"https://schema.org/size",
"https://schema.org/color"
],
"hasVariant": [
{
"@type": "Product",
"sku": "TB-2200-BLK-9",
"size": "9",
"color": "Black",
"offers": {
"@type": "Offer",
"price": "129.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
},
{
"@type": "Product",
"sku": "TB-2200-BLU-10",
"size": "10",
"color": "Blue",
"offers": {
"@type": "Offer",
"price": "129.00",
"priceCurrency": "USD",
"availability": "https://schema.org/OutOfStock"
}
}
]
}Each variant carries its own SKU, price, and availability, so one variant selling out doesn't misrepresent the others. The alternative, collapsing everything into a single Product, throws away the per-variant accuracy that makes the markup useful, and it's the most common mistake on variant-heavy catalogues.
Google supports variants for both single-page setups (all variants on one URL) and multi-page setups (each variant on its own URL). The markup differs slightly between them, so check the current documentation for your structure.
Enhancements worth adding
Beyond the core, a few optional properties unlock additional display treatments and are cheap to add if the data exists on your page.
Pros and cons. For editorial and review-style product pages, Google supports pros and cons markup that can surface in results. It uses positiveNotes and negativeNotes, and it's specifically intended for pages that editorially review a product rather than for the merchant's own product page. If you run a review or comparison site, this is directly relevant; if you're the merchant selling the item, it generally isn't the right fit.
Energy efficiency ratings. For product categories where energy labels apply, appliances, electronics in certain regions, hasEnergyConsumptionDetails lets you express the energy efficiency class and scale. This matters most for regulated markets where the rating is a purchase consideration, and it can appear as an annotation on the listing.
Price drop and price history. You don't mark these up directly, Google computes price drops by observing your price over time. What you can do is keep price accurate and current so the observation is correct. There's no property that manufactures a price-drop badge; there's only accurate pricing that Google may choose to annotate.
3D and AR models. For categories where it applies, subjectOf referencing a 3D model can support richer product experiences. This is niche and worth pursuing only if you already produce 3D assets.
The general principle with enhancements: add the ones whose data genuinely exists on your page and reflects reality, and skip the rest. An enhancement property with invented or approximate data is worse than its absence, because it risks the mismatch that invalidates the whole block.
Shipping and returns: the fields most stores skip
This is where the gap between an average implementation and a complete one is widest, and it's worth the effort because shipping and return details are exactly what shoppers compare.
Return policy. Nested in the offer as hasMerchantReturnPolicy:
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "US",
"returnPolicyCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}One current detail people miss: Google made returnPolicyCountry a required field in the return policy markup, using the two-letter ISO 3166-1 alpha-2 country code. Return policy markup that predates this update may be incomplete without it, so it's worth auditing existing implementations.
Shipping. Nested in the offer as shippingDetails, using OfferShippingDetails. Free shipping is especially worth surfacing, since it's a strong purchase signal. Shipping markup can specify cost, destination, and delivery timing via handlingTime and transitTime.
A 2025 change worth knowing: Google now lets you provide shipping and return policies in two other ways beyond per-product markup, configuring them directly in Search Console settings (available to sites Google identifies as merchants), or via organization-level structured data that sets a policy once for the whole site rather than repeating it on every product. Note that Search Console settings take precedence over on-page structured data where both exist. For a store with a single consistent policy, the organization-level approach is far less repetitive than per-product markup.
WooCommerce specifics
Most WordPress stores run WooCommerce, and it changes the implementation calculus in one important way: the data is already structured.
Price, stock status, SKU, and often ratings live in WooCommerce as fields, which means schema can be generated from live store data rather than typed in by hand. This matters because product data changes constantly, a price update or a stock change should propagate to your markup automatically, and any approach relying on manually maintained JSON-LD will drift out of sync the moment it does.
WooCommerce itself outputs some basic structured data, but it's often incomplete for rich result purposes, thin on identifiers, variants, and the shipping and return fields above. That's the gap third-party schema tooling fills. Whatever you use, the test is whether it pulls from live WooCommerce data and keeps the markup current, rather than producing a static block you have to maintain. Our docs walk through connecting a WooCommerce store step by step.
Choosing an approach in WordPress
Concretely, you have three routes, and they suit different situations.
Hand-coded JSON-LD. Maximum control, and viable only for a handful of products that rarely change. On any real catalogue it fails the moment prices or stock shift, because static markup doesn't update itself. It's also where the returnPolicyCountry-style requirement changes bite: you have to notice them and edit by hand.
A general SEO or schema plugin. Coverage varies widely. Some output solid Product markup; many are thin on variants, identifiers, and the shipping and return fields that distinguish a merchant listing from a basic snippet. The test to apply: install it, generate markup for a variant product with a return policy, and run the result through the Rich Results Test. If variants or returns come back missing, the plugin isn't covering the high-value fields.
AI generation from live store data. Reads your WooCommerce fields and produces complete markup that stays synchronised as products change. This is the approach that scales, because it removes the maintenance burden that makes the other two decay. It's also what we build, so weigh that against our pricing if you're comparing options.
Whichever you choose, the decision criteria are the same: does it pull from live product data rather than a static snapshot, does it cover variants and shipping and returns rather than just price and availability, and does it let you validate before publishing. A tool that only handles name, price, and availability gets you a basic product snippet and leaves the merchant listing, the richer, more competitive result, on the table.
Common mistakes
Price mismatch between markup and the visible page, the single most common cause of a suppressed result.
Marking up review data that isn't shown. aggregateRating must reflect visible reviews.
Flattening variants into one Product instead of a ProductGroup.
Stale availability, everything marked InStock regardless of reality.
Missing returnPolicyCountry in return policy markup, now required.
Category pages marked as a single Product. Product markup belongs on individual product pages; category and listing pages need different treatment.
JavaScript-injected markup. For merchant listings especially, markup belongs in the initial HTML, not rendered client-side where a crawler may miss it. If you're on a headless or custom stack, our Next.js schema guide covers this failure mode.
Inconsistency with a Merchant Center feed. If you run Google Merchant Center, consistency between your structured data and your feed is an explicit approval factor, conflicting data between the two causes problems.
A note on AI shopping surfaces
Product data is being consumed beyond Google's traditional results, and it's worth understanding what that does and doesn't change.
AI-generated answers increasingly appear on shopping queries, and the systems producing them lean on structured product data to understand price, availability, specifications, and comparisons. A catalogue with complete, accurate, consistent Product markup is easier for these systems to interpret than one where that information lives only in page copy or images.
Two honest caveats. First, no markup guarantees inclusion in an AI shopping answer any more than it guarantees a rich result, Google's own guidance is that no special structured data is required for its AI features. Second, what helps here is the same thing that helps everywhere: completeness and accuracy across the whole catalogue, not a special AI-specific property. There isn't one.
The practical takeaway is reassuring rather than alarming: the Product markup you implement for merchant listings is the same markup that makes your products legible to AI shopping surfaces. You're not doing separate work for each. Get the catalogue's structured data complete and consistent, and you've served both at once. Entity clarity, accurate identifiers, consistent brand, complete offers, is what carries across.
Validate before and after
Google's Rich Results Test checks eligibility for both product snippets and merchant listings, and includes variant validation. Run representative products through it, a simple product, a variant group, and one with shipping and returns, not just one example.
Search Console has separate Product snippets and Merchant listings reports that show how Google parsed your markup across the whole catalogue after crawling. This surfaces systematic issues a single-URL test won't, and it's where you'll catch a template bug affecting hundreds of pages.
The Schema Markup Validator at validator.schema.org handles generic syntax validation.
Check the current list of supported rich results periodically too, Product and its shopping features are actively developed, and requirements shift more often here than in most schema types.
Frequently Asked Questions
AI Schema Gen generates complete Product schema, variants, identifiers, shipping, and returns, from your live WooCommerce data, and validates it before publish. Start free or see the ecommerce use case.
Generate perfect schema in 30 seconds
AI Schema Gen handles everything automatically, free to start.
Get Started Free