Skip to content

Instantly share code, notes, and snippets.

@bakura10
Last active April 28, 2024 16:29
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save bakura10/75b03f0d92d73581bd8b0df7dc3c2db4 to your computer and use it in GitHub Desktop.
Save bakura10/75b03f0d92d73581bd8b0df7dc3c2db4 to your computer and use it in GitHub Desktop.
This is the last microdata-schema for our Shopify themes
{%- comment -%}
This snippet structures the micro-data using JSON-LD specification. Please note that for Product especially,
the schema often changes. We try to output as much info as possible, but Google may add new requirements over time,
or change the format of some info
LAST UPDATE: May 10th 2023 (we added the "hasMerchantReturnPolicy" and "shippingDetails" to include the shipping and
return policy if they have been specified as store policies).
{%- endcomment -%}
{%- if request.page_type == 'product' -%}
{%- assign days_product_price_valid_until = 10 | times: 86400 -%}
{%- capture main_entity_microdata -%}
{%- assign is_valid_global_gtin_length = false -%}
{%- if product.selected_or_first_available_variant.barcode != blank -%}
{%- assign gtin_string_length = product.selected_or_first_available_variant.barcode | size -%}
{%- if gtin_string_length == 8 or gtin_string_length == 12 or gtin_string_length == 13 or gtin_string_length == 14 -%}
{%- assign is_valid_global_gtin_length = true -%}
{%- endif -%}
{%- endif -%}
"@type": "Product",
"productID": {{ product.id | json }},
"offers": [
{%- for variant in product.variants -%}
{%- assign is_valid_gtin_length = false -%}
{%- if variant.barcode != blank -%}
{%- assign gtin_string_length = variant.barcode | size -%}
{%- if gtin_string_length == 8 or gtin_string_length == 12 or gtin_string_length == 13 or gtin_string_length == 14 -%}
{%- assign is_valid_gtin_length = true -%}
{%- endif -%}
{%- endif -%}
{
"@type": "Offer",
"name": {% if product.has_only_default_variant %}{{ product.title | json }}{% else %}{{ variant.title | json }}{% endif %},
"availability": {%- if variant.available -%}"https://schema.org/InStock"{%- elsif variant.incoming -%}"https://schema.org/BackOrder"{% else %}"https://schema.org/OutOfStock"{%- endif -%},
"price": {{ variant.price | divided_by: 100.0 | json }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"priceValidUntil": "{{ 'now' | date: '%s' | plus: days_product_price_valid_until | date: '%Y-%m-%d' }}",
{%- if variant.sku != blank -%}
"sku": {{ variant.sku | json }},
{%- endif -%}
{%- if variant.barcode != blank -%}
{%- if is_valid_gtin_length -%}
"gtin": {{ variant.barcode | json }},
{%- else -%}
"mpn": {{ variant.barcode | json }},
{%- endif -%}
{%- endif -%}
{%- if shop.refund_policy.body != blank -%}
"hasMerchantReturnPolicy": {
"merchantReturnLink": {{ shop.refund_policy.url | prepend: request.origin | json }}
},
{%- endif -%}
{%- if shop.shipping_policy.body != blank -%}
"shippingDetails": {
"shippingSettingsLink": {{ shop.shipping_policy.url | prepend: request.origin | json }}
},
{%- endif -%}
"url": "{{ shop.url }}{{ product.url }}?variant={{ variant.id }}"
}{% unless forloop.last %},{% endunless %}
{%- endfor -%}
],
{%- if product.metafields.reviews.rating.value != blank and product.metafields.reviews.rating_count.value > 0 -%}
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "{{ product.metafields.reviews.rating.value }}",
"reviewCount": "{{ product.metafields.reviews.rating_count.value }}",
"worstRating": "{{ product.metafields.reviews.rating.value.scale_min }}",
"bestRating": "{{ product.metafields.reviews.rating.value.scale_max }}"
},
{%- endif -%}
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | json }},
"category": {{ product.type | json }},
"url": "{{ shop.url }}{{ product.url }}",
"sku": {{ product.selected_or_first_available_variant.sku | json }},
{%- if product.selected_or_first_available_variant.barcode != blank -%}
{%- if is_valid_global_gtin_length -%}
"gtin": {{ product.selected_or_first_available_variant.barcode | json }},
{%- else -%}
"mpn": {{ product.selected_or_first_available_variant.barcode | json }},
{%- endif -%}
{%- endif -%}
"image": {
"@type": "ImageObject",
"url": "https:{{ page_image | image_url: width: 1024 }}",
"image": "https:{{ page_image | image_url: width: 1024 }}",
"name": {{ page_image.alt | json }},
"width": "1024",
"height": "1024"
}
{%- endcapture -%}
{%- elsif request.page_type == 'article' -%}
{%- capture main_entity_microdata -%}
"@type": "BlogPosting",
"mainEntityOfPage": "{{ article.url }}",
"articleSection": {{ blog.title | json }},
"keywords": "{{ article.tags | join: ', ' }}",
"headline": {{ article.title | json }},
"description": {{ article.excerpt_or_content | strip_html | truncatewords: 25 | json }},
"dateCreated": "{{ article.created_at | date: '%Y-%m-%dT%T' }}",
"datePublished": "{{ article.published_at | date: '%Y-%m-%dT%T' }}",
"dateModified": "{{ article.published_at | date: '%Y-%m-%dT%T' }}",
"image": {
"@type": "ImageObject",
"url": "https:{{ page_image | image_url: width: 1024 }}",
"image": "https:{{ page_image | image_url: width: 1024 }}",
"name": {{ page_image.alt | json }},
"width": "1024",
"height": "1024"
},
"author": {
"@type": "Person",
"name": "{{ article.user.first_name | escape }} {{ article.user.last_name | escape }}",
"givenName": {{ article.user.first_name | json }},
"familyName": {{ article.user.last_name | json }}
},
"publisher": {
"@type": "Organization",
"name": {{ shop.name | json }}
},
"commentCount": {{ article.comments_count }},
"comment": [
{%- for comment in article.comments limit: 5 -%}
{
"@type": "Comment",
"author": {{ comment.author | json }},
"datePublished": "{{ comment.created_at | date: '%Y-%m-%dT%T' }}",
"text": {{ comment.content | json }}
}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
{%- endcapture -%}
{%- endif -%}
{%- capture breadcrumb_entity_microdata -%}
"@type": "BreadcrumbList",
"itemListElement": [{
"@type": "ListItem",
"position": 1,
"name": {{ 'general.home' | t | json }},
"item": "{{ shop.url }}"
}
{%- if request.page_type == 'product' -%}
{%- if collection -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ collection.title | json }},
"item": "{{ shop.url }}{{ collection.url }}"
}, {
"@type": "ListItem",
"position": 3,
"name": {{ product.title | json }},
"item": "{{ shop.url }}{{ product.url }}"
}
{%- else -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ product.title | json }},
"item": "{{ shop.url }}{{ product.url }}"
}
{%- endif -%}
{%- elsif request.page_type == 'collection' -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ collection.title | json }},
"item": "{{ shop.url }}{{ collection.url }}"
}
{%- elsif request.page_type == 'blog' -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ blog.title | json }},
"item": "{{ shop.url }}{{ blog.url }}"
}
{%- elsif request.page_type == 'article' -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ blog.title | json }},
"item": "{{ shop.url }}{{ blog.url }}"
}, {
"@type": "ListItem",
"position": 3,
"name": {{ blog.title | json }},
"item": "{{ shop.url }}{{ article.url }}"
}
{%- elsif request.page_type == 'page' -%}
,{
"@type": "ListItem",
"position": 2,
"name": {{ page.title | json }},
"item": "{{ shop.url }}{{ page.url }}"
}
{%- endif -%}
]
{%- endcapture -%}
{% if main_entity_microdata != blank %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
{{ main_entity_microdata }}
}
</script>
{% endif %}
{% if breadcrumb_entity_microdata != blank %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
{{ breadcrumb_entity_microdata }}
}
</script>
{% endif %}
{%- if request.page_type == 'index' -%}
{%- assign potential_action_target = request.origin | append: routes.search_url | append: "?q={search_term_string}" -%}
<script type="application/ld+json">
[
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": {{ shop.name | json }},
"url": {{ shop.url | append: page.url | json }},
"potentialAction": {
"@type": "SearchAction",
"target": {{ potential_action_target | json }},
"query-input": "required name=search_term_string"
}
},
{
"@context": "https://schema.org",
"@type": "Organization",
"name": {{ shop.name | json }},
{%- if shop.brand.logo -%}
"logo": {{ shop.brand.logo | image_url: width: shop.brand.logo.width | prepend: "https:" | json }},
{%- endif -%}
{%- if shop.brand.short_description -%}
"description": {{ shop.brand.short_description | json }},
{%- endif -%}
{%- if shop.brand.slogan -%}
"slogan": {{ shop.brand.slogan | json }},
{%- endif -%}
{%- if shop.brand.metafields.social_links.size > 0 -%}
"sameAs": [
{%- for social_link in shop.brand.metafields.social_links -%}
{{- social_link.last.value | json -}}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
],
{%- endif -%}
"url": {{ shop.url | append: page.url | json }}
}
]
</script>
{%- endif -%}
@tmchow
Copy link

tmchow commented Aug 24, 2022

They definitely do not use the entire vocabulary of schema.org. I agree with you teh most up to date one is the second one you linked to (https://search.google.com/test/rich-results).

@tmchow
Copy link

tmchow commented Aug 24, 2022

@bakura10 one other suggestion.

https://gist.github.com/bakura10/75b03f0d92d73581bd8b0df7dc3c2db4#file-microdata-schema-liquid-L22

ProductID should be text, no? (surrounded by quotes).

In my microdata spit out with your latest snippet, i'm getting this:

CleanShot 2022-08-24 at 00 39 03

@tmchow
Copy link

tmchow commented Aug 25, 2022

@bakura10 It looks like there is a tweak to be made for when you use product.url. According to shopify's docs, that is the relative URL, and in microdata we need to use absolute URLs. Example is on line 52 and 72.

@bakura10
Copy link
Author

For the product.ID it seems to be working with it being as an ID. I am a bit unsure about this, while I agree it should be better, using {{ product.id | json }} is just safer in case of Shopify change in the future the format and that this format requires escaping (highly improbable but we never know).

You are right about the URL, Google does not seem to report it as an error but it should be absolute ideally. I just updated the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment