Short answer
Complete your product markup without duplicating it. Align the page, JSON-LD and Merchant Center, then check prices, variants, reviews and policies.
The answer in detail
Use Product structured data in JSON-LD, or ProductGroup for a product with variants. Start with the name, image, price and currency; complete the barcode, availability, condition, reviews, shipping and returns. On Shopify, inspect what the theme already supplies before adding anything. The page, markup and Merchant Center feed must describe the same offer.
The benchmark
Google recommends JSON-LD and documents the properties for pages where customers can buy. The Shopify column below reports the source authors' October 20, 2026 inspection of Dawn and Horizon demos, rather than a guarantee for every theme.
| Property | Google status in the source audit | Native Shopify output observed |
|---|---|---|
name, image |
Required | Yes |
offers.price above zero, offers.priceCurrency |
Required | Yes, an offer per variant |
offers.availability |
Recommended, one value | Yes, using variant inventory |
gtin |
Recommended | Yes, when the variant barcode is filled in |
brand.name |
Recommended, one brand | Yes, from Vendor |
variesBy |
Recommended for variants | No |
itemCondition |
Recommended | No |
aggregateRating, review |
Recommended; rating must be visible | No; usually supplied by a review app |
shippingDetails, hasMerchantReturnPolicy |
Recommended | No |
priceValidUntil |
Recommended; an expired date can prevent display | No |
Dawn and Horizon call Shopify's structured_data Liquid filter. The source inspection found ProductGroup output for variants but no variesBy describing size or color. Paid and older themes can differ; inspect the actual storefront.
The rule
Keep the screen, markup and feed aligned. Google recommends using both structured data and a feed because they can verify each other. Complete the theme's existing entity rather than letting another app generate a contradictory product.
Only mark up information the customer can see. A rating must appear on the page that declares it. Shipping and return claims need an agreed operational policy before they can become reliable data.
The steps
- Inventory the output. Count
application/ld+jsonblocks on a product page. At one apparel brand observed by the authors, the theme supplied a ProductGroup without reviews while a review app supplied another Product with a rating; Search Console flagged duplication. See the duplicate-markup audit. - Fill the fields the theme reads. Add each variant's barcode so its GTIN can appear automatically. See EAN and GTIN. A
?variant=URL must display that variant's actual price and stock. - Complete the existing entity. Add
variesByanditemCondition; attach the visible rating to the existing product entity. Prefer initial HTML: Google warns that JavaScript-generated product markup can make Shopping crawls less frequent and reliable. - Declare general policies once. Google recommends organization-level shipping and return policies, with product-level exceptions. Merchant Center settings can take precedence. The fictional example below uses US and Canadian return destinations and a US shipping destination, with illustrative US-dollar charges. These are example settings, not legal defaults. Decide the return policy first.
- Check consistency monthly. Sample five products: does
offers.pricematch the screen, is an unavailable product marked available, and does the review count match? Merchant Center's automatic item updates can correct price and availability from the page or reject an item when they cannot establish them. - Validate edge cases. Run a sale item and an out-of-stock item through the Rich Results Test, then monitor Search Console.
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Example brand",
"url": "https://www.example.com",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": ["US", "CA"],
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
},
"hasShippingService": {
"@type": "ShippingService",
"shippingConditions": [
{
"@type": "ShippingConditions",
"shippingDestination": {"@type": "DefinedRegion", "addressCountry": "US"},
"orderValue": {"@type": "MonetaryAmount", "minValue": 0, "maxValue": 49.99, "currency": "USD"},
"shippingRate": {"@type": "MonetaryAmount", "value": 5.90, "currency": "USD"},
"transitTime": {"@type": "ServicePeriod", "duration": {"@type": "QuantitativeValue", "minValue": 2, "maxValue": 3, "unitCode": "DAY"}}
},
{
"@type": "ShippingConditions",
"shippingDestination": {"@type": "DefinedRegion", "addressCountry": "US"},
"orderValue": {"@type": "MonetaryAmount", "minValue": 50, "currency": "USD"},
"shippingRate": {"@type": "MonetaryAmount", "value": 0, "currency": "USD"},
"transitTime": {"@type": "ServicePeriod", "duration": {"@type": "QuantitativeValue", "minValue": 2, "maxValue": 3, "unitCode": "DAY"}}
}
]
}
}
The pitfall
At that apparel brand, the real obstacle was conflicting copy. Banners, FAQs and product accordions promised free shipping above €39 to France and Belgium; the shipping policy charged Belgian shoppers €4.95 below €59. Product templates promised “within 48 hours,” the FAQ said two to three business days, and JavaScript calculated a third deadline.
Nobody could identify which promise to encode. Assign an owner to each delivery promise and return period, usually customer service or legal, then derive the screen, markup and feed from that decision. The delivery promise also affects conversion.
What to tell your team
“Of our five bestsellers, three declare a return policy that conflicts with our terms and two lack barcodes. Half a day aligns the page, markup and Merchant Center; then we check five products monthly.” These figures are fictional.
