Shopify Schema Markup: What Actually Earns Rich Results Now
FAQ rich results are gone. What still earns them: the structured_data Liquid filter, Product vs ProductGroup, and the required merchant listing fields.
Half of what you have been told about Shopify schema markup is now false
Shopify schema markup advice ages badly, because the thing it depends on — which structured data types Google actually renders as a rich result — changes without warning and the blog posts do not.
Two concrete examples of advice that was correct and is not any more:
- FAQPage. Google narrowed FAQ rich results in September 2023 so that they only showed for well-known, authoritative government and health sites. It then removed the feature from search results entirely in May 2026 and retired the documentation the following month. Every "add an FAQ app to get rich results" post is now selling you nothing.
- HowTo. Same story, earlier.
Meanwhile the thing that does still earn rich results on an ecommerce store got easier, because Shopify now emits it for you. This post is the current state.
Start by finding out what your theme already emits
Modern Shopify themes do not hand-write product JSON-LD any more. Dawn emits it with one line:
<script type="application/ld+json">
{{ product | structured_data }}
</script>
structured_data is a real Liquid filter and it is worth understanding precisely, because almost nobody writing about Shopify SEO mentions it. It converts an object into schema.org structured data, it works on the product and article objects, and the output type depends on the data:
- A product without variants outputs as a schema.org
Product. - A product with variants outputs as a
ProductGroup. - An article outputs as an
Article.
That Product versus ProductGroup split matters. ProductGroup is how you tell Google that six URLs are colours of one thing rather than six competing products, and getting variant relationships right is the difference between a clean merchant listing and a catalogue that looks duplicated from the outside.
So the first job on any store is not "add schema." It is: view source on a product page, find every application/ld+json block, and count them. I routinely find three — one from the theme, one from an SEO app, and one hardcoded into theme.liquid by whoever came before. They disagree about price. That is worse than having none, because you have handed a crawler a contradiction and let it choose.
What Google actually requires
For merchant listing experiences — the price, availability and shipping detail that shows next to a product result — the required properties are short:
On Product:
nameimageoffers
On the Offer:
price(orpriceSpecification.price)priceCurrency(orpriceSpecification.priceCurrency), a three-letter ISO 4217 code
And one constraint that catches people: merchant listing experiences require a price greater than zero. Products priced at 0 as a "call for pricing" placeholder — extremely common on trade and made-to-order catalogues — are not eligible, and no amount of markup fixes that. If you sell that way, the honest answer is that merchant listings are not your channel and you should put the effort into the collection and content pages instead.
Everything beyond those fields is optional enrichment: sku, gtin, brand, availability, itemCondition, aggregateRating, review, shipping and return policy details. Optional does not mean useless — a listing with GTIN and brand matches to Google's product knowledge graph far more reliably — but it does mean you should fix required-field failures before you go looking for enrichment.
The JSON-LD bugs I keep finding in themes
If your theme or an agency hand-builds JSON-LD instead of using the filter, there are two failure modes that produce valid-looking markup with wrong values. Shopify documents both, and I have found both in production.
1. Filter order in string concatenation. This looks harmless:
{%- assign json_price = '"price": "' | append: product.price | money_without_currency -%}
The money_without_currency filter applies to the whole concatenated string, not to the price. The string parses as 0.0 and the block silently renders 0.00. Your product page says 89.00 and your structured data says 0.00, and nothing in the theme errors.
2. Money filters in a schema price. Never use money_without_currency for a schema.org price at all. It formats using the store's currency settings, so a locale that uses a comma as the decimal separator produces an invalid price value. Output a plain decimal instead:
{%- for product in collection.products -%}
<script type="application/ld+json">
{
"@type": "Product",
"name": {{ product.title | json }},
"price": "{{ product.price | divided_by: 100.0 }}"
}
</script>
{%- endfor -%}
Note | json on the title. Product titles contain quotes, ampersands and apostrophes, and an unescaped title is how a whole JSON-LD block becomes unparseable — which fails silently, because an invalid block is simply ignored.
Both of these are the same underlying lesson: structured data fails quietly. There is no console error, no visual change, no 500. The only way you find out is by testing.
What is still worth marking up
Given FAQ and HowTo are gone, here is where I still spend the effort on a Shopify store:
Product / ProductGroup. The one that earns money. Emit it from Liquid with the source of truth being product fields and metafields, not an app that reads your rendered HTML.
BreadcrumbList. Still rendered by Google, and cheap. It should match your actual collection hierarchy, which means if your breadcrumbs are generated from the referring collection rather than a real hierarchy, fix the hierarchy first.
Organization, sitewide, with logo, sameAs links to your real social profiles, and contact details. This is the entity record. It does not produce a rich result on its own and it is disproportionately what answer engines quote when someone asks whether your brand is legitimate.
Article on blog posts, which you get from {{ article | structured_data }}.
FAQPage — still yes, but for a different reason. It no longer earns a rich result. It does still give a machine a clean, unambiguous question-and-answer structure on the page, and question-and-answer content is what gets lifted into AI answers and summaries. Mark it up because it makes the content parseable, not because you expect a blue accordion in the SERP. Anyone selling you FAQ schema as a rich-result play in 2026 has not checked in three years.
Review and AggregateRating — carefully. Reviews must be genuinely on the page, genuinely from customers, and genuinely about the thing being marked up. Self-serving markup on a page with no visible reviews is a manual-action risk, and the upside is not worth it.
How to actually verify
Three checks, in this order, every time:
- Rich Results Test on the live URL, not on pasted code. Pasted code tests your JSON. The URL test catches the app that injects a second conflicting block after page load.
- Search Console's Merchant listings and Product snippets reports. These show what Google saw at crawl time across the whole site, which is the only way to catch the one template out of six that is broken.
- View source and count `ld+json` blocks. Manually. Once per template type. It takes two minutes and it is the check that finds the duplicates.
And the thing to internalise: structured data is a claim about your data, so it can only be as correct as your data. A product with a missing GTIN, an inconsistent brand value and a blank product type will produce technically valid markup that describes an incoherent product. Catalogue hygiene comes first — that is why bulk catalogue operations and structured data are the same job on a store of any size.
The short version
- Find out what your theme already emits before you add anything. Count the
ld+jsonblocks. - Use
{{ product | structured_data }}unless you have a specific reason not to. It handles the Product versus ProductGroup distinction for you. - Required for merchant listings:
name,image,offers, withpriceandpriceCurrency, and the price has to be above zero. - Never build a schema price with a money filter. Output a plain decimal.
- FAQ and HowTo rich results are gone. Keep FAQPage for machine readability, drop it from your rich-result plan.
- Test the live URL, not the snippet.
Related services
Structured data is one half of Shopify SEO expert work; clean product data is the other, which is Matrixify catalog operations. Theme-level implementation lives under Shopify expert development. Related reading: Shopify canonical URLs and the duplicate content trap.
I implement structured data at the Liquid and metafield level, not with an app that guesses from your HTML. See [Shopify SEO expert services](/shopify-seo-expert) or [hire a Shopify developer](/hire-shopify-developer).
Direct: [WhatsApp +55 11 98851-2788](https://wa.me/5511988512788) · [contato.matheusabrahao@gmail.com](mailto:contato.matheusabrahao@gmail.com)
Need a senior engineer who thinks like an operator?
I take on a small number of Shopify operations and senior engineering engagements each quarter. If your store needs catalog hygiene, technical SEO, performance, or marketing automation done right — let's talk.
Continue reading
Shopify Canonical URLs: The Duplicate Content Trap in Collections and Filters
How Shopify canonical URLs actually work, why collection-scoped product URLs and filter parameters multiply your index, and the theme-level fixes that hold.
llms.txt on Shopify Is Already Built: agents.md, UCP, and Being Citable by AI
Shopify now serves llms.txt, llms-full.txt and agents.md for every store. What each file is, how to override them, and what GEO work is actually left to do.