Product Schema for Shopify: Field-by-Field Template

Product Schema for Shopify: Field-by-Field Template

January 10, 2025

Product schema is the single most impactful structured data type for any Shopify store. It's what puts your price, availability, and star ratings directly in Google search results. It's what AI shopping assistants parse when recommending products. And it's what Google Merchant Center increasingly requires for Shopping listings.

This guide gives you a complete, copy-paste JSON-LD template with every field explained, common mistakes flagged, and validation steps included. For the broader structured data picture, see Structured Data for Shopify: What to Add. For the complete schema markup overview, see the Shopify Schema Markup Guide.

Why Product Schema Matters More Than Any Other Schema Type

Product schema powers three outcomes that directly impact revenue:

Rich results in Google Search. When your product schema is valid and complete, Google can display your price, availability status, review stars, and shipping information directly in search results. This is the difference between a plain blue link and a listing that shows "$49.99 - In Stock - 4.8 stars (127 reviews)." The listing with product details gets clicked.

AI shopping integration. ChatGPT Shopping, Google AI Overviews, and Perplexity all extract product information from structured data. Incomplete schema means these systems either skip your products or represent them with missing details.

Google Merchant Center compliance. If you run Shopping campaigns or free product listings, Google expects structured data that matches your Merchant Center feed. Discrepancies between your schema and feed data cause disapprovals.

💡 Pro Tip: Analytics Agent automatically tracks all these metrics for you. Install Analytics Agent and get instant insights without the manual work.

Complete Product Schema Template (Copy-Paste Ready)

This template uses Shopify Liquid variables so product data stays dynamic. Place it in your product template file (usually sections/main-product.liquid or templates/product.liquid).

{% comment %}
  Product JSON-LD Schema for Shopify
  Place in: sections/main-product.liquid (before closing tag)
  Validates against: Schema.org Product + Google Rich Results requirements
{% endcomment %}

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": {{ product.title | json }},
  "description": {{ product.description | strip_html | truncate: 5000 | json }},
  "image": [
    {% for image in product.images limit: 5 %}
      {{ image | image_url: width: 1200 | prepend: 'https:' | json }}{% unless forloop.last %},{% endunless %}
    {% endfor %}
  ],
  "url": "{{ shop.url }}{{ product.url }}",
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  "brand": {
    "@type": "Brand",
    "name": {{ product.vendor | json }}
  },
  {% if product.metafields.custom.gtin %}
  "gtin13": {{ product.metafields.custom.gtin | json }},
  {% endif %}
  {% if product.metafields.custom.mpn %}
  "mpn": {{ product.metafields.custom.mpn | json }},
  {% endif %}
  "offers": {% if product.variants.size > 1 %}{
    "@type": "AggregateOffer",
    "lowPrice": {{ product.price_min | money_without_currency | json }},
    "highPrice": {{ product.price_max | money_without_currency | json }},
    "priceCurrency": {{ shop.currency | json }},
    "offerCount": {{ product.variants.size }},
    "offers": [
      {% for variant in product.variants %}
      {
        "@type": "Offer",
        "name": {{ variant.title | json }},
        "url": "{{ shop.url }}{{ variant.url }}",
        "sku": {{ variant.sku | json }},
        "price": {{ variant.price | money_without_currency | json }},
        "priceCurrency": {{ shop.currency | json }},
        "availability": "https://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}",
        "itemCondition": "https://schema.org/NewCondition"
      }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  }{% else %}{
    "@type": "Offer",
    "url": "{{ shop.url }}{{ product.url }}",
    "price": {{ product.price | money_without_currency | json }},
    "priceCurrency": {{ shop.currency | json }},
    "availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
    "itemCondition": "https://schema.org/NewCondition"
  }{% endif %}
}
</script>

After pasting this template, run a JSON-LD Audit to verify it validates correctly on your product pages.

Field-by-Field Breakdown

Required Fields

These fields must be present for Google to consider your Product schema valid.

name — The product title.

"name": {{ product.title | json }}

The | json filter handles escaping quotes and special characters automatically. Never hardcode product names.

image — At least one product image URL. Google recommends including multiple images.

"image": [
  {% for image in product.images limit: 5 %}
    {{ image | image_url: width: 1200 | prepend: 'https:' | json }}{% unless forloop.last %},{% endunless %}
  {% endfor %}
]

Use image_url: width: 1200 for high-resolution images. Google recommends images be at least 696px wide. Always prepend https: — Shopify's image URLs are protocol-relative by default.

description — A text description of the product.

"description": {{ product.description | strip_html | truncate: 5000 | json }}

Strip HTML tags and truncate to prevent oversized schema blocks. Google uses this for snippet generation.

offers — Price and availability information. This is the most complex required section and the one most frequently broken.

For a single-variant product:

"offers": {
  "@type": "Offer",
  "price": {{ product.price | money_without_currency | json }},
  "priceCurrency": {{ shop.currency | json }},
  "availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}"
}

For multi-variant products:

"offers": {
  "@type": "AggregateOffer",
  "lowPrice": {{ product.price_min | money_without_currency | json }},
  "highPrice": {{ product.price_max | money_without_currency | json }},
  "priceCurrency": {{ shop.currency | json }},
  "offerCount": {{ product.variants.size }}
}

Recommended Fields

Google doesn't require these, but they improve rich result quality and eligibility significantly.

brand — The product manufacturer or brand.

"brand": {
  "@type": "Brand",
  "name": {{ product.vendor | json }}
}

This is the most commonly missing field on Shopify stores. Shopify's product.vendor maps directly to the brand field. Google actively looks for this — its absence triggers a "missing recommended field" warning.

sku — The stock keeping unit.

"sku": {{ product.selected_or_first_available_variant.sku | json }}

url — The canonical URL for the product page.

"url": "{{ shop.url }}{{ product.url }}"

Always construct the full absolute URL. Relative URLs cause validation failures.

Optional But Valuable Fields

gtin13 / gtin12 / gtin — Global Trade Item Number (barcode).

{% if product.metafields.custom.gtin %}
"gtin13": {{ product.metafields.custom.gtin | json }},
{% endif %}

Google Merchant Center increasingly requires GTIN for product categorization. Store GTINs in Shopify metafields and pull them into schema conditionally.

mpn — Manufacturer Part Number.

{% if product.metafields.custom.mpn %}
"mpn": {{ product.metafields.custom.mpn | json }},
{% endif %}

If you don't have a GTIN, Google uses MPN + brand as a fallback identifier.

aggregateRating — Review summary data.

{% if product.metafields.reviews.rating %}
"aggregateRating": {
  "@type": "AggregateRating",
  "ratingValue": {{ product.metafields.reviews.rating.value | json }},
  "reviewCount": {{ product.metafields.reviews.rating_count | json }},
  "bestRating": "5",
  "worstRating": "1"
}
{% endif %}

The exact metafield path depends on your review app (Judge.me, Stamped, Loox, etc.). Check your app's documentation for the correct metafield namespace. Only include this if you actually have reviews — fabricated ratings violate Google's policies.

color, size, material — Product attributes from variant options.

{% for option in product.options_with_values %}
  {% if option.name == 'Color' %}
  "color": {{ option.selected_value | json }},
  {% endif %}
{% endfor %}
🔍

See Analytics Agent in Action

Discover how AI-powered insights can transform your Shopify store.

Learn More →

Handling Variants in Product Schema

Shopify products frequently have multiple variants (size, color, material). There are two valid approaches:

Approach 1: AggregateOffer (recommended for most stores)

Wrap all variants in an AggregateOffer with lowPrice and highPrice. This is simpler and what Google's documentation recommends for products with a price range.

Approach 2: Individual Offers per variant

List each variant as a separate Offer inside the AggregateOffer. This gives Google more detail but produces larger schema blocks. The complete template above uses this approach for maximum data coverage.

Key rule: Every variant that appears on the page must be represented in your schema. If a user can select "Large / Blue" on your product page, that variant should have a corresponding Offer in your JSON-LD.

Common Product Schema Mistakes on Shopify

Missing Brand Field

Shopify themes regularly omit the brand property. Google flags this as a "missing recommended field" and it reduces your eligibility for product rich results.

Before (broken):

{
  "@type": "Product",
  "name": "Blue Oxford Shirt",
  "offers": { ... }
}

After (fixed):

{
  "@type": "Product",
  "name": "Blue Oxford Shirt",
  "brand": {
    "@type": "Brand",
    "name": "Your Brand"
  },
  "offers": { ... }
}

The fix takes one line. Use {{ product.vendor | json }} to pull from Shopify's vendor field.

Wrong priceCurrency Format

The priceCurrency field must use ISO 4217 codes: USD, EUR, GBP, CAD. Not $, not dollars, not US Dollar.

Wrong:

"priceCurrency": "$"

Correct:

"priceCurrency": "USD"

Using {{ shop.currency | json }} pulls the correct ISO code automatically from your Shopify settings.

Missing Availability

Every Offer must include an availability URL from Schema.org's enumeration. The three you'll use:

  • https://schema.org/InStock
  • https://schema.org/OutOfStock
  • https://schema.org/PreOrder
"availability": "https://schema.org/{% if variant.available %}InStock{% else %}OutOfStock{% endif %}"

Missing availability is the second most common validation error on Shopify product pages.

Hardcoded Prices

Never put static prices in your JSON-LD. When you change a product's price in Shopify admin, hardcoded schema doesn't update. Google sees a mismatch between your visible price and schema price, and may suppress your rich results.

Always bind to Liquid:

"price": {{ product.price | money_without_currency | json }}

Mismatched Data

Your schema must match what users see on the page. If the visible price is $49.99 but your schema says $39.99 (perhaps from a sale that ended), Google considers this misleading. The fix: dynamic Liquid variables that always reflect current product state.

Google Merchant Center Schema Requirements

If you use Google Shopping (free listings or paid), Google Merchant Center has additional schema expectations beyond basic Product schema:

GTIN is required for most product categories. If you sell branded products, Google expects a barcode. Store these in Shopify metafields.

Product condition — add "itemCondition": "https://schema.org/NewCondition" (or UsedCondition, RefurbishedCondition).

Shipping details — Google is rolling out shippingDetails as a recommended property:

"shippingDetails": {
  "@type": "OfferShippingDetails",
  "shippingRate": {
    "@type": "MonetaryAmount",
    "value": "0",
    "currency": "USD"
  },
  "deliveryTime": {
    "@type": "ShippingDeliveryTime",
    "handlingTime": {
      "@type": "QuantitativeValue",
      "minValue": 0,
      "maxValue": 1,
      "unitCode": "DAY"
    },
    "transitTime": {
      "@type": "QuantitativeValue",
      "minValue": 3,
      "maxValue": 7,
      "unitCode": "DAY"
    }
  },
  "shippingDestination": {
    "@type": "DefinedRegion",
    "addressCountry": "US"
  }
}

Return policyhasMerchantReturnPolicy signals your return terms to Google:

"hasMerchantReturnPolicy": {
  "@type": "MerchantReturnPolicy",
  "applicableCountry": "US",
  "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
  "merchantReturnDays": 30,
  "returnMethod": "https://schema.org/ReturnByMail",
  "returnFees": "https://schema.org/FreeReturn"
}

Validating Your Product Schema

After implementing the template, validate every product page type (single variant, multi-variant, out of stock, on sale).

Step 1: Google Rich Results Test — paste a product page URL. Look for a "Product" result with all fields detected. Warnings are worth fixing; errors must be fixed.

Step 2: Check multiple pages. A template that works on one product might break on another (e.g., a product with no images, no vendor, or no variants). Test edge cases.

Step 3: Search Console — after Google crawls your updated pages, check Enhancements > Product for site-wide issues.

Step 4: Bulk validation — for stores with hundreds or thousands of products, manual testing doesn't scale. Run a JSON-LD Audit to validate product schema across your entire catalog at once. The audit flags missing fields, invalid values, and formatting errors per product.

💡 Pro Tip: Analytics Agent automatically tracks all these metrics for you. Install Analytics Agent and get instant insights without the manual work.

Before and After — Fixing a Broken Product Schema

Before (common Shopify theme output):

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Blue Oxford Shirt",
  "image": "//cdn.shopify.com/shirt.jpg",
  "offers": {
    "@type": "Offer",
    "price": "49.99"
  }
}

Issues: Missing brand, missing availability, missing priceCurrency, protocol-relative image URL, missing url.

After (fixed):

{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Blue Oxford Shirt",
  "image": "https://cdn.shopify.com/shirt.jpg",
  "url": "https://yourstore.com/products/blue-oxford-shirt",
  "brand": {
    "@type": "Brand",
    "name": "Your Brand"
  },
  "offers": {
    "@type": "Offer",
    "price": "49.99",
    "priceCurrency": "USD",
    "availability": "https://schema.org/InStock",
    "itemCondition": "https://schema.org/NewCondition",
    "url": "https://yourstore.com/products/blue-oxford-shirt"
  }
}

Five fields added. Zero ambiguity. Full rich result eligibility.

Don't want to fix these manually across every product? Run a JSON-LD Audit — it detects these exact issues and can auto-patch missing fields like brand, priceCurrency, and availability in one click.

Next Steps

  1. Copy the complete template from this guide into your product template
  2. Customize the review app metafield paths for your specific setup
  3. Add GTIN/MPN metafields if you have barcode data
  4. Run a JSON-LD Audit to validate across your catalog
  5. Check the Rich Results eligibility guide to understand what Google actually displays

Product schema is the foundation of your store's structured data strategy. Get this right and everything else — FAQ schema, breadcrumbs, organization markup — builds on a solid base.

Ready to Unlock Your Analytics Potential?

Connect Analytics Agent to your Shopify store and start making data-driven decisions today.

Get Started Free