Shopify Structured Data: A Deep-Dive Guide With Copy-Paste Code for Every Schema Type
What structured data (JSON-LD) does for a Shopify store, and copy-paste Liquid code for Product, BreadcrumbList, Organization, BlogPosting, ItemList, and FAQPage schema.

Structured data is a standard way to label what a page contains, so machines such as Google and AI systems can interpret it without guessing. On Shopify, it’s usually added to your theme as JSON-LD. Product information is the most important place to start.
Before you add anything: check what your theme already outputs. Dawn and many other Online Store 2.0 themes include structured data, often including Product markup. Open a product page, view its source, and search for application/ld+json. You can also search your theme files for that string. Adding another Product block without checking can create duplicate or conflicting data.
The short answer
For most Shopify stores, prioritize:
- Product: accurate product, variant, price, and availability information.
- BreadcrumbList: describes a page’s place in your site structure.
- Organization: identifies your store and, optionally, its social profiles.
- BlogPosting: describes your blog articles.
- FAQPage: can label visible FAQs, but Google no longer shows FAQ rich results.
- ItemList: describes a collection’s product list, but does not guarantee a special Google result.
Structured data can help Google understand your pages and can make product information easier for other systems to parse. It does not guarantee a rich result, higher rankings, or inclusion in an AI shopping experience. Start by checking what your theme already outputs, then add only what’s missing.
A quick primer: JSON-LD in Shopify
JSON-LD is a block of structured data written inside a <script type="application/ld+json"> tag on the page itself. Visitors never see it; machines read it. In a Shopify theme, you add it as a snippet and render that snippet from the template that needs it.
Product: the schema type that matters most
Product markup states a product’s name, image, description, SKU, brand, price, currency, and availability in a form machines can read. It is the foundation for product rich results in Google and the clearest way to state product facts to AI agents reading your store.
Create snippets/schema-product.liquid and render it from sections/main-product.liquid (or your theme’s product section), only after confirming your theme doesn’t already output Product schema.
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": {{ product.title | json }},
"image": [
{{ product.featured_image | image_url: width: 1200 | prepend: "https:" | json }}
],
"description": {{ product.description | strip_html | json }},
"sku": {{ product.selected_or_first_available_variant.sku | json }},
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"offers": {
"@type": "Offer",
"url": {{ shop.url | append: product.url | json }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"price": {{ product.selected_or_first_available_variant.price | divided_by: 100.0 | json }},
"availability": {% if product.available %}"https://schema.org/InStock"{% else %}"https://schema.org/OutOfStock"{% endif %}
}
}
</script>
Shopify stores prices in cents, so the divided_by: 100.0 converts the value to a decimal price. The json filter safely formats each Liquid string so quotes or special characters don’t break the JSON. Availability uses a full Schema.org URL.
This example uses product.available for availability. On a product with mixed in-stock and sold-out variants, that can differ from the availability of the selected variant. Test a few variants before publishing.
Adding genuine review ratings
Only add an aggregate rating if it reflects real customer reviews shown for that product. The example below reads Shopify’s product rating metafields. Place it inside the Product object, before the "offers" property, so the comma after the rating object is followed by another property.
{% if product.metafields.reviews.rating.value != blank %}
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": {{ product.metafields.reviews.rating.value | json }},
"reviewCount": {{ product.metafields.reviews.rating_count | json }}
},
{% endif %}
Shopify’s Product Reviews app and several review apps write to these metafields, but not every app does. If your ratings don’t appear in the metafields, ask your app’s support where they’re stored. Never invent or hardcode ratings. Review widgets that appear only after JavaScript runs may not be included in the page’s initial structured data.
BreadcrumbList: show where the product sits
BreadcrumbList describes a path such as Home → Collection → Product. It can help machines understand the page hierarchy; Google may show breadcrumb information in search, but appearance is not guaranteed.
This version assumes a product is being viewed through a collection URL. Positions are sequential: Home is 1, Collection is 2, and Product is 3. Add it to a product snippet rendered from sections/main-product.liquid, only if your theme doesn’t already provide suitable breadcrumbs.

<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": {{ shop.url | json }}
},
{
"@type": "ListItem",
"position": 2,
"name": {{ collection.title | json }},
"item": {{ shop.url | append: collection.url | json }}
},
{
"@type": "ListItem",
"position": 3,
"name": {{ product.title | json }}
}
]
}
</script>
This assumes the product is being viewed through a collection URL. A product opened directly at /products/... may not have a collection context. Many themes handle the no-collection case already, which is another reason to check existing output first.
Organization: identify your store
Organization markup describes the business behind the site. It can help search engines identify your store and logo, but it does not guarantee a particular search display.
Create snippets/schema-organization.liquid and render it from layout/theme.liquid before </body>, wrapped in {% if template == "index" %} so it appears on the homepage only. Add your logo in Shopify’s brand settings and test the rendered URL.

<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Organization",
"name": {{ shop.name | json }},
"url": {{ shop.url | json }},
"logo": {{ shop.brand.logo | image_url: width: 512 | prepend: "https:" | json }}
}
</script>
To include social profiles, add a sameAs array listing your social profile URLs as plain quoted strings, for example: "sameAs": ["https://www.instagram.com/yourstore", "https://www.facebook.com/yourstore"]. Use your real profile URLs.
BlogPosting: describe your articles
BlogPosting describes a blog article, including its headline, author, dates, image, and publisher. It gives machines useful context about the article; do not treat it as a guarantee of a search enhancement.
Create a snippet such as snippets/schema-article.liquid and render it in the article template or the section that outputs each blog post. In many themes, that’s a section such as sections/main-article.liquid.
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "BlogPosting",
"headline": {{ article.title | json }},
"description": {{ article.excerpt_or_content | strip_html | truncate: 300 | json }},
"image": [
{{ article.image | image_url: width: 1200 | prepend: "https:" | json }}
],
"datePublished": {{ article.published_at | date: "%Y-%m-%dT%H:%M:%SZ" | json }},
"dateModified": {{ article.updated_at | date: "%Y-%m-%dT%H:%M:%SZ" | json }},
"author": {
"@type": "Person",
"name": {{ article.author | json }}
},
"publisher": {
"@type": "Organization",
"name": {{ shop.name | json }}
},
"mainEntityOfPage": {{ shop.url | append: article.url | json }}
}
</script>
Check that the article image and author are present, and that the published and modified dates match what your theme displays.
ItemList: describe a collection page
ItemList describes a list of products and their positions. It can give machines context about a collection’s contents, but it is not a replacement for Product markup on individual product pages and does not guarantee a collection rich result.
Create snippets/schema-collection-itemlist.liquid and render it from your collection template or product-grid section, such as sections/main-collection-product-grid.liquid.
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "ItemList",
"itemListElement": [
{% for product in collection.products %}
{
"@type": "ListItem",
"position": {{ forloop.index }},
"url": {{ shop.url | append: product.url | json }},
"name": {{ product.title | json }}
}{% unless forloop.last %},{% endunless %}
{% endfor %}
]
}
</script>
The {% unless forloop.last %},{% endunless %} condition adds commas between entries, but not after the final one. This keeps the generated JSON valid. If your collection is paginated, remember that the code describes the products rendered on that page, not necessarily every product in the collection.
FAQPage: use only for visible FAQs
FAQPage labels questions and answers on a page. Google stopped showing FAQ rich results for most sites back in 2023, so don’t add this markup expecting a Google FAQ enhancement. Other parsers may interpret it, but no AI assistant is required to use it. Clear, visible answers still matter most.
Add this to an FAQ page template or the section that outputs the FAQs. Replace the sample questions and answers with your store’s real wording, and make sure the text matches the visible content exactly.
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How long does shipping take?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Orders ship within 2 business days and arrive in 3-5 business days in the US. You can track every order from your shipping confirmation email."
}
},
{
"@type": "Question",
"name": "What is your return policy?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Unworn items can be returned within 30 days of delivery for a full refund. Start a return from the link in your order email."
}
}
]
}
</script>
How to test and ship your changes
Before publishing, duplicate your theme and test the preview version. Then:
- Open a live or preview page and use Google’s Rich Results Test. For schema types it doesn’t report, use validator.schema.org.
- View the rendered page source and search for every
application/ld+jsonblock. Confirm there is no duplicate Product schema and that product data matches the visible page. - Check price, currency, availability, image, and variant information on more than one product.
- Publish during a quieter period, then recheck the live page.
- If something breaks, remove the snippet’s render call to roll back the change.
Validation confirms that markup is readable; it does not confirm Google will show a rich result. Keep your page content and structured data consistent, and revisit the code when you change themes, apps, product data, or review providers.
Frequently asked questions
Does Shopify add structured data automatically?
Many Shopify themes include some structured data, but exactly what they output varies by theme and setup. Check your rendered page source before adding schema, especially on product pages.
Where do I paste JSON-LD in Shopify?
Create a snippet for the schema type and render it from the matching template or section: product schema in the product section, article schema in the article section, and so on. Duplicate your theme first so you can test and roll back safely.
What structured data types matter most for ecommerce?
Start with accurate Product data, then check BreadcrumbList and Organization markup. BlogPosting, FAQPage, and collection ItemList may be useful depending on your content, but don’t add them just to increase the number of schema types on your store.
Can I use an app instead of editing code?
Yes, but check what the app adds and whether your theme already outputs the same schema. An app can create duplicate markup, so test the rendered page source after installing it.
How do I test structured data on Shopify?
Use Google’s Rich Results Test for eligible Google features and Schema Markup Validator for general Schema.org validation. Also inspect the page source yourself to confirm that the data is present, accurate, and not duplicated.
Will structured data help me rank in ChatGPT or AI search?
Structured data can make product facts easier for machines to parse, but there is no guarantee that a particular AI system will use it or that it will improve visibility. Accurate product pages, crawlability, and clear store information matter too; see how AI shopping agents discover products.
If you’re unsure what your theme or apps already output, a Shopify audit can review structured data alongside crawlability and AI readiness. If you need help implementing or cleaning up the code, explore Shopify development or contact Built Best.