E-commerce Insights

Why your product schema isn't showing rich results (and how to fix it for shopify and custom platforms)

Why your product schema isn't showing rich results (and how to fix it for shopify and custom platforms)

I often get messages from store owners who are frustrated: they’ve added product schema, tested it in Google’s Rich Results Test, and still their products don’t show up with rich results in the SERP. I’ve seen this dozens of times across Shopify stores and custom platforms. The good news is that it’s almost never magic — usually it’s one or more avoidable issues. Below I walk through the common reasons product schema doesn’t trigger rich results and practical fixes for both Shopify and custom platforms.

Why product schema can fail to produce rich results

From experience, the most frequent culprits are:

  • Missing required properties. Google expects specific fields for Products (name, image, offers.price, offers.priceCurrency, availability, etc.). If any required item is missing or malformed, Google won’t show a rich result.
  • Conflicting or duplicated markup. Two different schema implementations on the same page (microdata + JSON-LD or multiple JSON-LD blocks with conflicting values) confuse crawlers.
  • Offers mismatch or missing offers. If the product page doesn’t contain clear, indexable pricing or availability, rich results won’t appear. Sometimes JavaScript renders price after load and Google can’t read it.
  • Indexing or crawling blocks. Noindex tags, robots.txt rules, or blocked resources (like JS/CSS needed to render content) prevent Google from seeing the markup.
  • Structured data errors flagged in Search Console. You might have markup that passes the Rich Results Test but Search Console still flags issues for live pages or site-level problems.
  • Not eligible due to policy or quality. Thin content pages, pages with misleading information, or pages with troubleshooting policy issues can be disqualified.
  • Low or no review data. Some product-rich features (like review snippets) require aggregateRating. Without it, you won’t get that specific enhancement.
  • Checklist: what to verify immediately

  • Is the product page indexable? Use URL Inspection in Search Console.
  • Does the page contain a single, consistent JSON-LD product block (or a single microdata implementation)?
  • Does offers include price, priceCurrency, availability (Use schema.org values like "InStock")? Is currency consistent with the site?
  • Are images accessible (not blocked by robots) and of sufficient size?
  • Are canonical tags pointing to the correct product page?
  • Does Search Console show structured data errors under Enhancements → Product?
  • Is the content served via JavaScript? If so, is it renderable by Google (server-side rendering or dynamic rendering)?
  • Required and recommended Product properties (quick reference)

    Required name, image, offers (price, priceCurrency, availability)
    Highly recommended description, sku, brand, url, aggregateRating, review

    Note: Google’s documentation evolves, but those basics rarely change. Having a complete set of fields improves eligibility.

    Fixes specific to Shopify

    I work with Shopify stores a lot, so here are concrete steps that tend to solve most problems:

  • Prefer JSON-LD in theme templates. Add a single JSON-LD block in your product.liquid or product.json template rather than patching multiple apps that inject microdata. In most modern themes there is a product schema snippet already — check it and standardize.
  • Use Liquid variables so markup reflects actual product data. Example placeholders: {{ product.title }}, {{ product.price | money_without_currency }}, {{ shop.currency }}. Make sure they output the raw values (no HTML) and match your visible content.
  • Ensure priceCurrency uses the ISO 4217 code. Shopify’s money filters sometimes output symbols. Use currency settings or explicit variables to output "USD", "EUR", etc.
  • Watch out for apps that inject schema. Many apps add their own product or review schema. Remove duplicates or consolidate into the theme-level JSON-LD.
  • Test server vs client rendering. If your theme renders price via AJAX on load, Google might not see it. Either render initial price in HTML/Liquid or use dynamic rendering for bots.
  • If you need a minimal JSON-LD snippet for Shopify, embed it in your product template and populate with Liquid variables:

    Example (conceptual) JSON-LD for Shopify

    { "context": "https://schema.org", "type": "Product", "name": "{{ product.title }}", "image": ["{{ product.featured_image | img_url: 'master' }}"], "description": "{{ product.description | strip_html | escape }}", "sku": "{{ product.sku }}", "mpn": "{{ product.variants.first.sku }}", "brand": { "type": "Brand", "name": "{{ product.vendor }}" }, "offers": { "type": "Offer", "url": "{{ shop.url }}{{ product.url }}", "priceCurrency": "{{ shop.currency }}", "price": "{{ product.price | divided_by: 100 }}", "availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}" } }

    Adapt the filters to your theme’s price formatting. The key is consistent, server-side values — not values injected purely by client-side JS.

    Fixes for custom platforms

    For custom-built sites, you have more control but also more responsibility. Here’s my pragmatic workflow:

  • Generate JSON-LD server-side. Avoid client-side construction for required properties. Return a single, authoritative JSON-LD block inside the page head or body so crawlers can parse it immediately.
  • Follow schema.org types strictly. Use correct URIs for availability and correct currency codes for priceCurrency.
  • Include complete offer information. If you support multiple offers (marketplaces, multiple sellers), use an appropriate Offers array and make sure each offer has price & currency.
  • Expose review data where appropriate. If you show ratings, include aggregateRating with count and value.
  • Run the Rich Results Test and Search Console Live Test. Don’t rely on “it looks fine” — Google’s tools will show exactly what they see.
  • Example JSON-LD for a custom site (simplified):

    { "context":"https://schema.org", "type":"Product", "name":"Acme Running Shoes", "image":["https://example.com/images/shoe1.jpg"], "description":"Lightweight running shoes", "sku":"ACM-RUN-01", "brand":{ "type":"Brand", "name":"Acme" }, "aggregateRating":{ "type":"AggregateRating", "ratingValue":"4.5", "reviewCount":"29" }, "offers":{ "type":"Offer", "url":"https://example.com/product/acme-shoe", "priceCurrency":"USD", "price":"79.99", "availability":"https://schema.org/InStock" } }

    Debugging tips I use all the time

  • Run the URL through the Rich Results Test and the Schema Markup Validator. Compare outputs.
  • Use Search Console’s URL inspection to fetch as Google — look for blocked resources, indexing issues, or differing render results.
  • Check page source for duplicate JSON-LD blocks. If duplicates exist, remove extras or reconcile conflicting values.
  • Look at server logs or GA/GTM events to confirm Googlebot actually fetched the page and rendered JS if needed.
  • If you use a CDN or caching layer, purge caches after schema changes. Cached HTML can hide fixes.
  • Be patient: even after fixing markup, Google may take time to pick up changes and decide to show rich results.
  • If you want, send me one product URL and tell me whether you’re on Shopify or a custom stack. I’ll review the structured data output and point to the exact field or implementation that needs fixing — that’s often the fastest way to get rich results showing again.

    You should also check the following news: