A properly structured product schema template ensures your eCommerce listings communicate price, availability, reviews, and SKU data directly to Google's rich results pipeline. This guide walks through the essential properties, how to choose between Offer vs AggregateOffer, common validation traps, and when to layer Product on top of LocalBusiness or Organization markup.
Google's structured-data testing tool will flag three mandatory fields: name, image, and a nested Offer object. The name should match your H1 or primary product title verbatim to avoid ambiguity. Image must be a publicly accessible URL—relative paths and data URIs fail validation. The Offer object itself requires price as a decimal string (no currency symbols), priceCurrency in ISO 4217 format (CAD, USD, EUR), and availability using schema.org enumerations like InStock, OutOfStock, or PreOrder.
Beyond the minimum, description is technically optional but strongly recommended; Google often pulls snippet text from this field when your meta description is thin. Brand as an Organization or Brand entity helps with Shopping Graph associations, especially if you manufacture your own line. SKU and GTIN identifiers improve matching when your products appear on aggregator sites or comparison engines, though they are not strictly required for markup to validate.
A single Offer works when you sell one SKU at one price point. The moment you introduce variants—small, medium, large T-shirts each with their own price or availability—you need AggregateOffer as the parent, with individual Offer children for each variant. AggregateOffer rolls up the low and high price (lowPrice, highPrice) so Google can display a range in the snippet.
Common mistake: hardcoding a single Offer when your cart actually shows size or colour dropdowns. Google sees the mismatch between markup and rendered DOM, then ignores the schema. If you drop-ship or aggregate inventory from multiple suppliers, AggregateOffer also lets you list separate seller entities. For pure dropshipping where you never touch inventory, you can still use AggregateOffer and point each child Offer to the supplier's availability endpoint, though maintaining real-time sync is the operational challenge.
The review and aggregateRating properties are powerful but heavily policed. Google will suppress or penalize product markup if it detects self-authored reviews, incentivized ratings, or review snippets that do not correspond to genuine user submissions. Each Review object should include author (Person or Organization), datePublished, and reviewRating with a ratingValue and bestRating.
AggregateRating summarizes the overall score: ratingValue (the average), ratingCount (total reviews), and bestRating (your scale's ceiling, typically five). If you run a Shopify store using a third-party review app like Judge.me or Yotpo, confirm the app outputs valid schema; many early plugins emitted malformed JSON-LD. For businesses in regulated verticals—legal, medical, financial—be cautious about embedding testimonials in product markup; Google's guidelines distinguish between product reviews and professional-service endorsements, and conflating them can trigger manual actions.
PriceCurrency must match the currency your checkout actually charges. Canadian stores that display CAD but process in USD for international customers should either emit separate Product+Offer blocks for each geo (using hreflang signals) or set priceCurrency to the default checkout currency and handle conversions in JavaScript. The priceValidUntil property is optional but recommended for time-sensitive promotions; omitting it means Google assumes the price is indefinite, which can cause stale snippet data if you run frequent sales.
Shipping and tax nuances: schema.org allows shippingDetails and hasMerchantReturnPolicy, but Google's rich-result renderer does not yet surface these in organic snippets—they matter more for Merchant Center feeds and Shopping ads. If you operate in Quebec, ensure French product names and descriptions are present in alternate-language schema blocks or on separate /fr/ URLs; Google.ca will pull from the language variant that matches the searcher's locale.
Run your JSON-LD through Google's Rich Results Test and Schema Markup Validator before going live. The most frequent errors: image URLs that 404 or redirect, price formatted with commas or currency symbols, availability strings that do not match schema.org enumerations, and orphaned @id references that point to non-existent entities. Once deployed, monitor Search Console's Enhancements report under Products; items marked "Valid with warnings" may still render but could lose eligibility if warnings escalate to errors.
For large catalogs, template your schema server-side using your CMS or headless commerce platform's native fields—Shopify Liquid, WooCommerce PHP hooks, or Next.js getStaticProps. Hardcoding hundreds of product schemas in static HTML is unmaintainable. If you inject schema via Google Tag Manager, confirm it fires on initial page load, not after user interaction; Google's crawler executes JavaScript but may not wait for deferred tags. Finally, update your schema whenever you change pricing, discontinue a SKU, or add new review data; stale markup risks suppression and erodes user trust when the snippet promises stock you no longer carry.
Product schema does not exist in isolation. A local bike shop selling frames and accessories should nest Product inside a parent LocalBusiness entity, linking via mainEntity or @graph. This tells Google that the business both offers services (repairs, fittings) and sells physical goods, improving the chances of dual rich-result eligibility—local pack plus product carousel.
Similarly, if your product page lives under a broader category or collection, you can wrap Product in a CollectionPage or ItemList schema, using itemListElement to enumerate related SKUs. This hierarchy helps Google understand taxonomy and can influence how your site appears in refinement filters. For subscription or SaaS products, consider whether Product is even the right type; if you sell licenses or memberships, Service or Offer with a recurring billing structure may be more semantically accurate. The key is aligning schema types with the user's search intent and the transactional flow your page actually supports.
If variants share the same product name and differ only in attributes like size or colour, use one Product entity with AggregateOffer as the parent and individual Offer children for each variant. If variants have distinct SKUs that function as separate products (e.g. different models), emit separate Product blocks or use ItemList to group them.
Yes. Nest the Product entity inside your primary LocalBusiness or Organization schema using mainEntity or include it in an @graph array. This signals that the business offers both services and tangible products. Just ensure the product markup reflects actual checkout data—price, availability, SKU—and is not promotional fluff.
Update the availability property to OutOfStock and leave the schema in place. Removing markup entirely can cause rich-result loss and confuse Google about whether the page is still relevant. If the product is discontinued permanently, consider a 301 redirect to a similar item or category page and deprecate the schema on the old URL.
Schema.org has a priceSpecification property that supports tiered pricing, but Google's rich-result renderer largely ignores it. The safest approach is to mark up the default public price in the Offer and handle member discounts in your cart logic. If you must show a range, use AggregateOffer with lowPrice and highPrice, but be prepared for the snippet to display the range rather than a single figure.
No. Merchant Center ingests product data via XML or CSV feeds, not schema markup. However, on-page Product schema can improve organic rich results and drive click-through independent of your Shopping campaigns. Running both in parallel—structured feeds for ads, JSON-LD for organic—is common practice for eCommerce sites.
Technically yes, but Google expects the schema to reflect transactional data if you mark it as a Product with Offer. For editorial reviews, use Review schema with itemReviewed pointing to a Product entity that contains basic name and image fields but no Offer. This avoids misleading users into thinking they can purchase directly from your review page.