Most Shopify stores have incomplete structured data, broken rich snippets, and schema gaps that AI crawlers skip entirely. The products are fine. The markup is not.
This guide covers the schema types that still drive visible results in 2026, what changed with the deprecation of FAQ rich results in May 2026, and the exact steps to implement Product schema and review stars on your Shopify store, whether you edit Liquid or prefer an app.
What rich snippets actually are, and what Shopify gives you by default
Rich snippets are the extra details that sometimes appear under a search result: star ratings, price, availability, and shipping information. You do not produce them directly. You add structured data to your pages, and Google decides whether to show a rich result based on that data, the user's intent, and your website's SEO performance.
Modern themes, including Dawn version 15.0 and above, output built-in schema markup using the structured_data Liquid filter. That covers the basics: product name, price, and availability.
What Shopify does not cover is the markup that actually drives visible rich results in 2026: aggregateRating for star ratings, shippingDetails, hasMerchantReturnPolicy, brand, GTIN, and the full Merchant Listing field set that Google now requires for Shopping surfaces.

How structured data works in Google Search. Image taken from Google’s official documentation on Structured Data
Test Your Rich Results
Before you add anything, run the Google Rich Results Test on one of your product pages. Paste the URL, wait for the results, and note which fields are detected, missing, or flagged. That is your baseline. Everything in this article builds from there.

Google Rich Results test above recognizes FAQs as a valid item on May 13th, 2026, even though the dropdown preview won’t appear in SERP after May 7th.
FAQ rich results are gone. Here is what happened

The announcement was a small note at the top of the FAQ structured data developer documentation. No advance warning beyond the three-year phase-out that most stores never tracked.
The timeline of the FAQ rich results deprecation
What does this mean for your existing FAQ schema?
If the FAQ section on your product pages contains genuine, useful answers to real customer questions, the markup should stay. If it was filler added to inflate SERP real estate, this is a good time to clean up both the content and the schema.
The FAQPage type remains valid schema.org markup, and AI crawlers still use it to answer user questions and link back to your product.
A well-optimized product page that includes one FAQ: 'Can this tent fit 4 people comfortably?' would increase the chances of AI overview crawling content from your website.
Given the nature of AEO (Answer Engine Optimization), where users ask long, natural-language questions, and the fact that AI overviews now appear in 47% of Google search results, FAQs that address your users’ questions could be the most valuable content on your product pages.
If you want to go deeper on what AI-driven discovery means for your product pages, this conversation is worth 10 minutes
Shero Commerce CEO Beth Shero is joined by Cande Rybecky and Molly Siston, co-founder of somi, to discuss what AI-driven product discovery means for eCommerce brands.
The two product-rich result types
Google now distinguishes between two classes of product-rich results, each with different requirements. Most implementation guides treat them as the same thing. They are not.
-
Product Snippets are available to any page that describes a product, including editorial reviews and comparison pages. They focus on ratings, reviews, and basic pricing. These are the star ratings and price ranges that appear under organic search listings.

-
Merchant Listings are only available on pages where a customer can actually buy the product. They unlock Shopping panel appearances, Popular Products, price drop alerts, and product carousels. The required fields are stricter: name, image, and offers with price, priceCurrency, and availability. You also need product identifiers: GTIN, MPN, or SKU. Without identifiers, Google cannot match your product to its catalog or compare it across sellers.

A Shopify product page can qualify for both simultaneously. Adding the full Merchant Listing field set also satisfies Product Snippet requirements.
When to add which?
If a customer can buy the product on that page, implement Merchant Listing schema. If you are writing a review or comparison post, use Product Snippet schema.
How to add Product schema to Shopify
There are two paths: manual JSON-LD via Liquid for stores with developer access, and apps for everyone else. Both can produce a valid schema.
The manual route gives you more control over variants, dynamic pricing, and custom logic. Apps are faster and require no coding knowledge, but they carry the risk of duplicating schema if your theme already generates it.
While we are mainly focused on FAQ rich results and Review Stars on this piece, the tool below will provide the know-how and ready-made snippets for all Shopify and eCommerce structured data. Get back to it whenever you need guidance.
Shopify Rich Snippets and Schema Glossary
Pick a schema type
to get started
Each entry explains what it does, whether Shopify already covers it, and exactly how to implement it — with or without a developer.
Schema types, ranked by search impact
Paths for each: app-based or manual code
Common mistakes that silently break rich results
Product
Product schema is the foundation everything else builds on. It tells Google — and AI search engines like Perplexity and Google AI Overviews — the basic facts about what you sell: name, description, image, SKU, and brand.
In 2026 Google distinguishes between two classes of product rich result. Product Snippets show star ratings and basic pricing under organic listings. Merchant Listings unlock Shopping panel appearances, price drop alerts, and carousels — but only when you also include a GTIN, MPN, or SKU identifier. A fully built Product schema qualifies for both simultaneously.
- Product name
- Featured image
- Price
- In / out of stock status
- Brand / vendor
- SKU / GTIN / MPN identifiers
- Description
- ShippingDetails
- MerchantReturnPolicy
- AggregateRating
Schema Plus for SEO
Covers Product, AggregateRating, BreadcrumbList, and more. Detects and removes duplicate schema from your theme automatically. Includes an onboarding call.
Ilana's JSON-LD for SEO
Established since 2015. Handles legacy theme conflicts well. Active personal support. Best choice if you have messy existing schema from a previous theme or app.
Nabu Schema Pro
Free app by AdNabu. Covers 6 schema types including Product. Good starting point for smaller stores wanting solid baseline coverage at no cost.
sections/main-product.liquid (Dawn and modern themes) or templates/product.liquid (older themes). Search the file for ld+json or structured_data. If found, your theme already outputs schema — extend it rather than adding a second block on top.</body> tag. Save, then test immediately with the Rich Results Test — do not wait for Google to re-index.<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | truncate: 500 | json }},
"image": [
{% for image in product.images %}
"https:{{ image | img_url: 'master' }}"{% unless forloop.last %},{% endunless %}
{% endfor %}
],
"sku": {{ product.selected_or_first_available_variant.sku | json }},
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"offers": {
"@type": "Offer",
"url": "{{ shop.url }}{{ product.url }}",
"priceCurrency": "{{ shop.currency }}",
"price": "{{ product.selected_or_first_available_variant.price | money_without_currency | remove: ',' }}",
"priceValidUntil": "{{ 'now' | date: '%s' | plus: 31536000 | date: '%Y-%m-%d' }}",
"availability": "https://schema.org/{% if product.selected_or_first_available_variant.available %}InStock{% else %}OutOfStock{% endif %}",
"itemCondition": "https://schema.org/NewCondition"
}{% if product.metafields.reviews.rating.value %},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "{{ product.metafields.reviews.rating.value }}",
"reviewCount": "{{ product.metafields.reviews.rating_count }}"
}{% endif %}
}
</script>
aggregateRating block is conditional — it only outputs if you have Shopify's native review metafields populated. If you use a review app like Judge.me, the app injects its own aggregateRating block and you should remove that conditional to avoid duplication.
-
✓Go to search.google.com/test/rich-results, paste a product URL, and confirm Product is detected with no errors or warnings.
-
✓Check that the price shown in the test exactly matches the visible price on the page. Any mismatch causes Google to remove the rich result.
-
✓Look for a SKU field in the test output. If it's absent, you won't qualify for Merchant Listings.
Offer
Offer is not a standalone schema type — it lives inside your Product block. It tells Google the exact price, currency, stock status, and condition of the item. This is what makes your price appear directly in search results before someone clicks.
Adding the two missing fields — shippingDetails and hasMerchantReturnPolicy — unlocks delivery time and return window display in Google search results, which Shopify does not include by default.
availability field must also be dynamic. Hardcoding InStock when items go out of stock is a policy violation that Google actively penalises with demotion or removal of the rich result.
| remove: ',' — or use | json which outputs a clean numeric string — to avoid invalid output.
"offers": {
"@type": "Offer",
"url": "{{ shop.url }}{{ product.url }}",
"priceCurrency": "{{ shop.currency }}",
"price": "{{ product.selected_or_first_available_variant.price | money_without_currency | remove: ',' }}",
"priceValidUntil": "{{ 'now' | date: '%s' | plus: 31536000 | date: '%Y-%m-%d' }}",
"availability": "https://schema.org/{% if product.selected_or_first_available_variant.available %}InStock{% else %}OutOfStock{% endif %}",
"itemCondition": "https://schema.org/NewCondition"
}
priceValidUntil is computed dynamically by adding 31,536,000 seconds (one year) to today's Unix timestamp, then formatting as a date. This avoids the Search Console warning that appears when priceValidUntil is set to a past date.
ProductGroup
If you sell a t-shirt in 5 sizes and 4 colours, you have 20 variants. Without ProductGroup schema, Google sees them as separate unrelated items — or ignores them entirely. With it, all variants are grouped under one parent product entity, which gives Google a clean picture of your full offering.
This matters for Merchant Listings. Google can then show a price range, display colour and size options in search results, and correctly match your product across Shopping surfaces and price comparison.
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "ProductGroup",
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | truncate: 500 | json }},
"url": "{{ shop.url }}{{ product.url }}",
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"variesBy": ["https://schema.org/size", "https://schema.org/color"],
"hasVariant": [
{% for variant in product.variants %}
{
"@type": "Product",
"name": {{ product.title | append: ' - ' | append: variant.title | json }},
"sku": {{ variant.sku | json }},
"image": "https:{{ product.featured_image | img_url: 'master' }}",
"offers": {
"@type": "Offer",
"priceCurrency": "{{ shop.currency }}",
"price": "{{ variant.price | money_without_currency | remove: ',' }}",
"availability": "https://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}",
"url": "{{ shop.url }}{{ variant.url }}"
}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
</script>
AggregateRating
AggregateRating is what produces the star ratings visible under product listings in Google search results. It requires either a live review app or reviews stored in Shopify metafields, and the actual review content must be visible on the page.
Judge.me
98% pass rate on the Google Rich Results Test — highest of any major review app. Free plan includes rich snippets. Clean schema that rarely breaks on theme updates. Best option for most stores.
Loox
94% pass rate. Best for photo reviews. Ideal for apparel, beauty, and home goods stores where customer photos drive conversion. Occasional widget customisation can break JSON-LD injection.
Yotpo
92% pass rate — lowest of the major apps, mostly from duplicate schema issues. Best if you also need loyalty programmes, SMS, or UGC tools in one platform.
{% if product.metafields.reviews.rating.value %}
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "{{ product.metafields.reviews.rating.value }}",
"reviewCount": "{{ product.metafields.reviews.rating_count }}"
}
{% endif %}
ShippingDetails
ShippingDetails lets Google display your delivery cost and estimated delivery window directly in search results, before someone clicks your listing. This is an opportunity to show "Free shipping · 2–5 days" next to your product title — a direct conversion lever that most Shopify stores miss.
It nests inside the Offer block and defines a shipping rate, destination country, handling time, and transit time.
JSON-LD Express for SEO
One of the few Shopify apps that explicitly generates ShippingDetails and ReturnPolicy schema. Built-for-Shopify certified. Merchants specifically cite video and shipping markup support in 2026 reviews.
"shippingDetails": {
"@type": "OfferShippingDetails",
"shippingRate": {
"@type": "MonetaryAmount",
"value": "0",
"currency": "{{ shop.currency }}"
},
"shippingDestination": {
"@type": "DefinedRegion",
"addressCountry": "GB"
},
"deliveryTime": {
"@type": "ShippingDeliveryTime",
"handlingTime": {
"@type": "QuantitativeValue",
"minValue": 0,
"maxValue": 1,
"unitCode": "DAY"
},
"transitTime": {
"@type": "QuantitativeValue",
"minValue": 2,
"maxValue": 5,
"unitCode": "DAY"
}
}
}
offers object in your Product schema. Update addressCountry to your shipping destination, set value to your shipping cost (0 for free shipping), and adjust the minValue / maxValue days to match your actual delivery window.
MerchantReturnPolicy
MerchantReturnPolicy tells Google your return window — for example "30-day free returns." Google can display this directly under search results before someone visits your store. In competitive product categories this is a meaningful click-through rate lever, particularly when a competitor's listing shows no returns information at all.
It nests inside the Offer block, alongside ShippingDetails. You define whether returns are accepted, the window in days, how items are returned, and whether there is a fee.
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "GB",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
merchantReturnDays to match your actual policy. For no-returns policies use MerchantReturnNotPermitted. For a final-sale policy use MerchantReturnUnlimitedWindow if returns are always accepted regardless of time.
Organisation
Organisation schema identifies your brand to Google. It's what makes your logo eligible to appear in the Knowledge Panel on branded searches. The sameAs array — linking to your Instagram, Facebook, and Wikidata entry — is what anchors your brand as a distinct entity in Google's Knowledge Graph, which in turn helps AI systems correctly attribute content to your brand rather than treating pages as anonymous.
Shopify includes a basic version in the theme, but the sameAs links are almost always missing and need to be added manually.
{% if template == 'index' %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}",
"logo": {
"@type": "ImageObject",
"url": "https:{{ 'logo.png' | asset_url }}"
},
"email": {{ shop.email | json }},
"sameAs": [
"https://www.instagram.com/yourbrand",
"https://www.facebook.com/yourbrand",
"https://twitter.com/yourbrand",
"https://www.linkedin.com/company/yourbrand"
]
}
</script>
{% endif %}
{% if template == 'index' %} wrapper ensures this only outputs on the homepage. Replace logo.png with your actual logo filename in the Shopify assets folder, and update each sameAs URL to your real social profiles.
Review
Review schema marks up individual customer reviews — reviewer name, rating value, date, and review text. This gives Google structured access to the detail behind your star ratings, which feeds into rich results and helps AI search engines cite product quality information when answering shopping queries.
In practice, every major review app handles Review schema output automatically when rich snippets are enabled. If you are already using Judge.me, Loox, Yotpo, or Stamped with rich snippets on, individual Review schema is already being output correctly alongside AggregateRating.
CollectionPage
CollectionPage schema marks up your category and collection pages, telling Google this page is a listing of products rather than a single product page. It also communicates your site hierarchy — which collections sit under which categories.
This doesn't produce a visual rich result you'll see directly in search, but it improves how Google indexes and ranks your collection pages over time and helps AI systems understand your full catalog structure.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": {{ collection.title | json }},
"description": {{ collection.description | strip_html | truncate: 300 | json }},
"url": "{{ shop.url }}{{ collection.url }}",
"hasPart": [
{% for product in collection.products limit: 12 %}
{
"@type": "Product",
"name": {{ product.title | json }},
"url": "{{ shop.url }}{{ product.url }}",
"image": "https:{{ product.featured_image | img_url: 'medium' }}"
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
</script>
ItemList
ItemList tells Google this page contains an ordered, curated list of products. This is the schema type that can produce product carousels in Google Search — rows of items with images and prices that appear above traditional organic results.
It works best on landing pages where you've specifically curated a selection, such as "Best Running Shoes Under £100" or "Gift Ideas for Her." It can be used alongside CollectionPage on the same page.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": {{ collection.title | json }},
"url": "{{ shop.url }}{{ collection.url }}",
"itemListElement": [
{% for product in collection.products %}
{
"@type": "ListItem",
"position": {{ forloop.index }},
"url": "{{ shop.url }}{{ product.url }}",
"name": {{ product.title | json }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
</script>
Article / BlogPosting
Article schema structures your blog content for Google and AI search engines. The most important benefit in 2026 is Google AI Overview inclusion — pages with properly structured Article schema including a named author and a clear publish date are significantly more likely to be cited in AI-generated answers than pages without it.
Shopify outputs a basic Article block, but the author field is usually just a plain string rather than a Person type with credentials. Adding the full block below replaces that with a version Google can properly attribute.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": {{ article.title | json }},
"description": {{ article.excerpt | strip_html | truncate: 200 | json }},
"image": "https:{{ article.image | img_url: 'master' }}",
"datePublished": "{{ article.published_at | date: '%Y-%m-%dT%H:%M:%S' }}",
"dateModified": "{{ article.updated_at | date: '%Y-%m-%dT%H:%M:%S' }}",
"author": {
"@type": "Person",
"name": {{ article.author | json }}
},
"publisher": {
"@type": "Organization",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}"
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{{ shop.url }}{{ article.url }}"
}
}
</script>
sections/main-article.liquid for ld+json first and replace the existing block rather than adding a second one.
FAQPage
However, Google has confirmed it continues reading FAQ structured data to understand page content. More relevantly for Shopify merchants: the retrieval systems behind Google AI Overviews, Perplexity, and ChatGPT search actively use FAQPage schema to extract Q&A content. Pages with well-written FAQ schema that answer genuine product questions — sizing, materials, shipping, returns — are more likely to be cited when an AI system answers a related query.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is your return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "We accept returns within 30 days of purchase. Items must be unworn and in their original packaging. Return postage is free."
}
},
{
"@type": "Question",
"name": "How long does delivery take?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Standard delivery takes 3 to 5 business days. Express delivery is available at checkout for next-day delivery."
}
}
]
}
</script>
VideoObject
VideoObject tells Google the metadata for a video on your product page — title, description, thumbnail, upload date, and duration. This makes the video eligible to appear in Google video search results and video carousels, which can drive additional discovery traffic beyond standard organic listings.
If you host your product videos on YouTube, Google often picks up the metadata automatically. Adding explicit VideoObject schema guarantees accuracy and ensures the video is associated with the correct product page.
JSON-LD Express for SEO
One of the very few Shopify apps that generates VideoObject schema. Merchants specifically cite video markup support in 2026 reviews alongside ShippingDetails and ReturnPolicy.
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "VideoObject",
"name": {{ product.title | append: ' — Product Demo' | json }},
"description": {{ product.description | strip_html | truncate: 200 | json }},
"thumbnailUrl": "https://img.youtube.com/vi/YOUR_VIDEO_ID/maxresdefault.jpg",
"uploadDate": "2026-01-01",
"duration": "PT2M30S",
"contentUrl": "https://www.youtube.com/watch?v=YOUR_VIDEO_ID",
"embedUrl": "https://www.youtube.com/embed/YOUR_VIDEO_ID"
}
</script>
YOUR_VIDEO_ID with your YouTube video ID. Set uploadDate to when the video was published. duration uses ISO 8601 format — PT2M30S means 2 minutes and 30 seconds.
LocalBusiness
LocalBusiness schema tells Google your physical store address, opening hours, phone number, and coordinates — the data that powers your appearance in local search results and Google Maps. Without it, Google has to infer this information from unstructured text on your page, which is less reliable.
If you are an online-only store, skip this schema type entirely. It does not apply.
{% if template == 'index' %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}",
"telephone": "+44-XXXX-XXXXXX",
"email": {{ shop.email | json }},
"address": {
"@type": "PostalAddress",
"streetAddress": "123 Your Street",
"addressLocality": "Your City",
"postalCode": "XX1 1XX",
"addressCountry": "GB"
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": [
"Monday","Tuesday","Wednesday",
"Thursday","Friday"
],
"opens": "09:00",
"closes": "18:00"
}
],
"geo": {
"@type": "GeoCoordinates",
"latitude": 51.5074,
"longitude": -0.1278
}
}
</script>
{% endif %}
Step 5. Save the file and test immediately using the Rich Results Test on a product URL or the ahrefs extension. Do not wait until it is indexed. The tester reads the live page and tells you immediately if anything is broken.
2. App-based schema (no code required)
If you are not comfortable editing Liquid files, an app is the right choice. Apps automatically inject Product schema using your existing product data and update it when prices or availability change.

JSON-LD for SEO is a commonly used Shopify app for adding product schema
Before installing any schema app, run the Rich Results Test on a product page. If your theme already outputs Product schema, and the app also outputs it, Google will see two conflicting blocks on the same page.
This is the most common schema error on Shopify stores, and it causes Google to ignore both blocks. Disable the theme's built-in schema before enabling the app, or use an app that automatically detects and handles theme conflicts.
How to add review stars to Shopify
Star ratings in Google search results come from the aggregateRating field in your Product schema. They matter, as studies and real-world data show that rich snippets with star ratings can improve CTR by 30% or more.
Adding review stars requires some manual work because Shopify's default theme schema does not include them.
You need a review app or a manual implementation. Without one of these, your listings will never show stars, regardless of how many reviews your products have.

How the aggregated rating structured data looks from the Ahrefs web extension
1. Via a review app
Judge.me, Yotpo, Loox, and Stamped all support aggregateRating schema output. The step is the same for all of them: go to the app settings, find the SEO or Google Integration section, and confirm that rich snippets or the AggregateRating Schema is enabled.
That toggle tells the app to inject the aggregateRating node into your Product schema with your live rating values and review count.
A view inside Judge.me dashboard
2. Without a review app
If you are not using a third-party review app but do have reviews stored in Shopify metafields, add the aggregateRating node directly to your Product schema block. The code in the example above includes a conditional: it only outputs the aggregateRating node if the product.metafields.reviews.rating value exists. This prevents Google from seeing empty or zero-value rating data, which can trigger a penalty.

How the liquid code looks in the Shopify admin
One rule that applies to all implementations
Only add the aggregateRating schema where actual customer reviews are visible on the page. A Schema that claims ratings exist when the page shows none is a policy violation. Google's bots check for consistency between markup and visible content.
Does your store have any gaps?
Structured data is not a 2026 trend. It is the foundation that search, and now AI, runs on.
The disappearance of FAQ rich results from SERPs is not a sign that schema matters less. It is a sign that the game has shifted. Google, Perplexity, and ChatGPT are all reading your store's structured data to decide whether to cite you, surface you, or buy directly from your store as AI agents become increasingly popular.
Most Shopify stores have enough schema to pass a surface check. But not enough to show up where AI answers have replaced the first three organic results. If you want a full audit, our SEO team can show you the gaps.
Talk to one of our SEO experts and get a full SEO audit that shows you exactly what to improve next.