Ecommerce SEO

How to Add Product Schema to Shopify Stores

R Ron Tsantker · · 4 min read
Our Digital Services

This would be the quick and easy way to add product schema code to your shopify store.

———

Go to Online Store > Themes > Edit Code

Choose the “product.liquid” file.

Add the following code towards the top:

———

<script type=’application/ld+json’>

{

“@context”: “http://schema.org/”,

“@type”: “Product”,

“name”: “{{ product.title }}”,

“url”: “{{ shop.url }}{{ product.url }}”,

“image”: “{{ shop.url }}{{ product.featured_image }}”,

“description”: “{{ page_description }}”,

“offers”: {

“@type”: “Offer”,

“priceCurrency”: “{{ shop.currency }}”,

“price”: “{{ product.price | money_without_currency }}”,

“itemCondition”: “http://schema.org/NewCondition”,

“availability”: “http://schema.org/InStock”

}

}

</script>


For a much more comprehensive schema, you can use the following:

{%- 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
{%- endcomment -%}

{%- if request.page_type == 'product' -%}
  {%- assign days_product_price_valid_until = 10 | times: 86400 -%}

  {%- assign gtin_option = 'gtin' -%}
  {%- if product.selected_or_first_available_variant.barcode != blank -%}
    {%- assign is_barcode_available = true -%}
    {%- 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_gtin_length = true -%}
      {%- assign gtin_option = gtin_option | append: gtin_string_length -%}
    {%- endif -%}
  {%- endif -%}

  {%- assign has_reviews = false -%}
  {%- assign rating_value = blank -%}
  {%- assign review_count = blank -%}
  {%- if product.metafields.spr.reviews != blank -%}
    {%- assign rating_value_review_array = product.metafields.spr.reviews | split: 'meta itemprop="ratingValue" content="' -%}
    {%- assign rating_value_arr = rating_value_review_array[1] |split: '"' -%}
    {%- assign rating_value = rating_value_arr[0] -%}
    {%- assign review_count_str_array = product.metafields.spr.reviews | split: 'meta itemprop="reviewCount" content="' -%}
    {%- assign review_count_arr = review_count_str_array[1] | split: '"' -%}
    {%- assign review_count = review_count_arr[0] -%}

    {%- unless review_count == blank -%}
      {%- assign has_reviews = true -%}
    {%- endunless -%}
  {%- endif -%}

  {%- capture main_entity_microdata -%}
    "@type": "Product",
    "offers": [
      {%- for variant in product.variants -%}
        {
          "@type": "Offer",
          "name": {{ variant.title | json }},
          "availability": {%- if variant.available -%}"https://schema.org/InStock"{%- else -%}"https://schema.org/OutOfStock"{%- endif -%},
          "price": {{ variant.price | money_without_currency | json }},
          "priceCurrency": {{ shop.currency | json }},
          "priceValidUntil": "{{ 'now' | date: '%s' | plus: days_product_price_valid_until | date: '%Y-%m-%d'}}",
          {%- if variant.sku != blank -%}
            "sku": {{ variant.sku | json }},
          {%- endif -%}
          "url": "{{ request.path }}{{ variant.url }}"
        }{% unless forloop.last %},{% endunless %}
      {%- endfor -%}
    ],
    {%- if is_barcode_available and is_valid_gtin_length %}
      "{{gtin_option}}": {{ product.selected_or_first_available_variant.barcode | json }},
      "productId": {{ product.selected_or_first_available_variant.barcode | json }},
    {%- elsif is_barcode_available %}
      "mpn": {{ product.selected_or_first_available_variant.barcode | json }},
      "productId": {{ product.selected_or_first_available_variant.barcode | json }},
    {%- endif %}
    {%- if has_reviews -%}
      "aggregateRating": {
        "@type": "AggregateRating",
        "ratingValue": "{{ rating_value }}",
        "reviewCount": "{{ review_count }}"
      },
    {%- endif %}
    "brand": {
      "name": {{ product.vendor | json }}
    },
    "name": {{ product.title | json }},
    "description": {{ product.description | strip_html | json }},
    "category": {{ product.type | json }},
    "url": "{{ request.path }}{{ product.url }}",
    "sku": {{ product.selected_or_first_available_variant.sku | json }},
    "image": {
      "@type": "ImageObject",
      "url": "https:{{ product.featured_image | img_url: '1024x' }}",
      "image": "https:{{ product.featured_image | img_url: '1024x' }}",
      "name": {{ product.featured_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:{{ article.image | img_url: '1024x' }}",
      "image": "https:{{ article.image | img_url: '1024x' }}",
      "name": {{ article.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.breadcrumb.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 | within: collection }}"
        }
      {%- 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": "http://schema.org",
    {{ main_entity_microdata }}
  }
  </script>
{% endif %}

{% if breadcrumb_entity_microdata != blank %}
  <script type="application/ld+json">
  {
    "@context": "http://schema.org",
    {{ breadcrumb_entity_microdata }}
  }
  </script>
{% endif %}

Build your own playbook

Stop reading. Start ranking.

If you've read this far you already care about doing SEO right. Drop your details below and we'll send the actual plan menu and a follow-up series with real case studies in your category.

  • Real starting prices in your inbox within 60 seconds
  • Real client case studies and the full pricing breakdown over a 2-week follow-up series
  • Optional 30-min strategy call — written proposal even if you don't buy
  • One-click unsubscribe. No 'reach out to your account manager' nonsense.

Or skip the email and reserve a 30-minute strategy call — we'll look at your top 3 competitors live and you walk away with a written proposal.

Free — no card required

Send me the actual pricing

We email you the actual starting prices tailored to your SEO focus, plus a short follow-up series with real client case studies and the full pricing breakdown. Unsubscribe with one click.

1Your focus
2Your business

We never sell your data. Used only to send pricing + the strategy guide.