Ecommerce SEO

Ecommerce Schema Markup: Product and Collection Structured Data

Schema markup is the structured data layer underneath your store that tells Google exactly what your products are — price, availability, reviews, brand — in a format it can read without guessing. Here is how to implement it on any ecommerce platform.

Sharkly Team March 14, 2026 14 min read

What is schema markup, and why does it matter for ecommerce?

Schema markup is structured data — a block of JSON code added to your page that describes your content in a vocabulary Google understands natively. For a product page, that means telling Google not just that you sell something called "Running Shoe Model X", but that it is a Product, it costs £89.99, it is currently InStock, it carries a 4.7-star aggregate rating from 312 reviews, and the brand is a specific organisation you can verify.

Without schema, Google has to infer all of this from your page content — reading your HTML, guessing at your price format, estimating your availability from copy like "ships in 2–3 days". With schema, you hand it the answer sheet directly.

The practical consequences are significant. Correct product schema enables rich results — the enhanced search listings that show star ratings, price, availability, and shipping information directly in Google's results page, before anyone clicks. Research consistently shows rich results generate meaningfully higher click-through rates than standard blue-link listings for the same ranking position. And as with meta descriptions, CTR feeds directly into Google's Navboost user-behaviour scoring system — which means schema improvements compound over time through the same mechanism that rewards any click-quality improvement.

This applies to every ecommerce stack: Shopify, WooCommerce, BigCommerce, Magento, Webflow, custom storefronts. The schema types are the same. What changes is how your platform outputs them — and where the defaults silently fall short.

How Google uses structured data — and what it actually unlocks

Before writing a line of theme code, it is worth being precise about what schema does and does not do for your rankings.

Schema markup is not a direct ranking factor in the sense that adding it will not, by itself, move your product pages up in the rankings. Google has been explicit about this. What schema does is:

  • Enable rich results eligibility. You cannot get price, availability, or review stars shown in your search listing without valid structured data. Google only shows these enhancements for pages that pass schema validation.

  • Improve click-through rate. A listing showing "£89.99 · ★★★★½ · In Stock" next to your competitors' plain blue links will generate more clicks for the same ranking position. Higher CTR → better Navboost signals → rankings that hold and improve over time.

  • Reduce Google's reliance on inference. When Google doesn't have to guess at your product data, it indexes your pages more confidently and surfaces them more consistently for relevant queries. Ambiguity in your content creates ambiguity in how you're ranked.

  • Enable Merchant Centre integration. If you connect your store to Google Merchant Centre (essential for Shopping ads), schema on your product pages acts as a second data layer that Merchant Centre cross-references. Discrepancies between your schema and your Merchant Centre feed cause disapprovals.

The bottom line: schema is infrastructure. It is not a quick-win tactic. Done correctly, it is the technical foundation that allows everything else — your content, your backlinks, your keyword targeting — to perform at full potential in a search results page that has increasingly rewarded structured product data.

What ecommerce platforms do automatically — and where they fall short

Most modern ecommerce platforms output some Product schema by default. Shopify themes use a structured_data Liquid filter. WooCommerce stores often inherit schema from Yoast, Rank Math, or the theme. BigCommerce and Magento ship baseline product markup. Custom and headless storefronts typically ship nothing unless you add JSON-LD yourself.

The problems begin immediately below that surface level — and they show up on every platform:

  • No aggregate reviews. Native product schema often omits AggregateRating. If you use a reviews app or plugin, your stars are on the page but not in your schema unless you explicitly add them or the reviews tool injects its own block. You are not getting review rich results without this.

  • No brand data. Default output frequently omits the brand property. Google uses this to understand product provenance, connect your products to brand entities in the Knowledge Graph, and serve brand-filtered searches correctly.

  • No GTIN / MPN / barcode. Product identifiers (Global Trade Item Numbers, Manufacturer Part Numbers) are powerful disambiguating signals. They allow Google to confirm your product is the same as the one described in external sources — product databases, manufacturer sites, review aggregators. Most platforms store these in variant or SKU fields, but they are not always output by default.

  • Price formatting errors. Some platforms store prices as integers in the smallest currency unit (8999 for £89.99). Outputting 8999 as the schema price fails Google's validation checks. Always emit a decimal in the buyer's currency.

  • Category and collection pages have no schema at all. Most platforms provide nothing for category pages by default. Adding ItemList schema to collections is entirely manual — but it is one of the highest-leverage schema additions for stores with a strong category SEO strategy.

  • Default markup cannot be customised. If you rely on a built-in filter or a SEO plugin's default Product block, you often cannot add extra properties without outputting a second Product schema block — which creates duplication warnings in Google's Rich Results Test.

The recommendation for any store taking SEO seriously: own a single Product JSON-LD block you fully control. Disable or replace the platform default if it cannot include brand, identifiers, reviews, and a correctly formatted offer. This is a one-time setup that pays dividends for the lifetime of the store.

You can generate valid JSON-LD for Product, CollectionPage, and BreadcrumbList with Sharkly's free schema markup generator, then paste it into your theme, plugin, or tag manager.

Product schema: the correct implementation

JSON-LD is Google's recommended format. Put one <script type="application/ld+json"> block in the product page HTML. Every dynamic value must be escaped as JSON. Every property must match what is visible on the page.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "Merino Wool Running Socks — 3-Pack",
  "description": "Ultra-soft merino wool running socks with arch support and moisture-wicking properties.",
  "url": "https://www.yourstore.com/products/merino-wool-running-socks",
  "image": [
    "https://www.yourstore.com/images/merino-socks-main.jpg",
    "https://www.yourstore.com/images/merino-socks-detail.jpg"
  ],
  "sku": "MW-SOCK-3PK-BLK",
  "gtin": "0123456789012",
  "brand": {
    "@type": "Brand",
    "name": "RunDeep"
  },
  "offers": {
    "@type": "Offer",
    "url": "https://www.yourstore.com/products/merino-wool-running-socks",
    "priceCurrency": "USD",
    "price": "34.99",
    "priceValidUntil": "2027-12-31",
    "itemCondition": "https://schema.org/NewCondition",
    "availability": "https://schema.org/InStock",
    "seller": {
      "@type": "Organization",
      "name": "Your Store Name"
    }
  },
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": "4.6",
    "reviewCount": "89",
    "bestRating": "5",
    "worstRating": "1"
  }
}
</script>

Generate this from your live product data — not hardcoded HTML — so price, availability, and reviews stay accurate when inventory changes.

Breaking down each property — and why it's there

name and description

These are the baseline. Strip HTML from descriptions before putting them in JSON-LD. Raw formatting tags produce invalid schema that Google's validator will reject. Truncate extremely long descriptions (around 5,000 characters) to prevent JSON parse errors.

image as an array

Google's product rich results guidelines specify that the image should be crawlable, large enough to trigger rich results (minimum 160px, recommended 1200px), and ideally provided as an array when multiple images are available. Use absolute https:// URLs. Protocol-relative URLs (//cdn.example.com/...) are technically valid in browsers but rejected by Google's schema validator.

brand

Including the vendor or manufacturer as a Brand entity is one of the most underused schema properties on ecommerce stores. It connects your products to brand entities in Google's Knowledge Graph, which is increasingly important for brand-specific search queries ("Nike running shoes", "Le Creuset casserole"). Omit the block entirely if brand is blank rather than outputting an empty string.

gtin / mpn / sku

If your products have barcodes — which most retail and wholesale products do — mapping them to gtin is the single highest-impact addition you can make beyond the baseline. GTINs allow Google to cross-reference your product against its product database and surface your listing in Shopping-integrated search results with far higher confidence. If a product has no barcode, omit the property. Do not fabricate GTINs.

offers — price formatting

Price must be a decimal in the listed currency, not an integer in cents. "price": 8999 will cause rich results to fail validation or show an incorrect price. priceValidUntil is required for price-bearing rich results. Set it dynamically to a future date you actually honour — never a past date.

aggregateRating

Review stars in search results are consistently among the highest CTR drivers for product pages. Only output this block when real reviews exist and the count is greater than zero. A ratingValue with no actual reviews is a spam signal. Your schema rating must match the visible review average on the page.

How to add this on common ecommerce platforms

Shopify

Modern Shopify themes (Dawn and later) include a built-in structured data output via the {{ product | structured_data }} Liquid filter. It handles name, description, price, and availability — and almost always misses reviews, brand, and GTINs.

Save a custom snippet as snippets/schema-product.liquid and include it in your product template with {% render 'schema-product' %}. If your theme uses {{ product | structured_data }}, find it in sections/main-product.liquid or templates/product.json and remove it first — or you will output two Product schema blocks for the same page.

{% comment %}
  Product schema — reviews, brand, GTINs.
  Save as: snippets/schema-product.liquid
  Include: {% render 'schema-product' %}
  Remove any {{ product | structured_data }} calls first.
{% endcomment %}
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": {{ product.title | json }},
  "description": {{ product.description | strip_html | truncate: 5000 | json }},
  "url": {{ shop.url | append: product.url | json }},
  "image": [
    {{ product.featured_image | image_url: width: 1200 | prepend: "https:" | json }}
    {% for image in product.images limit: 5 %}
      {% unless image == product.featured_image %}
        , {{ image | image_url: width: 1200 | prepend: "https:" | json }}
      {% endunless %}
    {% endfor %}
  ],
  "sku": {{ product.selected_or_first_available_variant.sku | json }},
  {% if product.vendor != blank %}
  "brand": {
    "@type": "Brand",
    "name": {{ product.vendor | json }}
  },
  {% endif %}
  {% assign variant = product.selected_or_first_available_variant %}
  {% if variant.barcode != blank %}
  "gtin": {{ variant.barcode | json }},
  {% endif %}
  "offers": {
    "@type": "Offer",
    "price": {{ variant.price | divided_by: 100.0 | json }},
    "priceCurrency": {{ cart.currency.iso_code | json }},
    "availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
    "itemCondition": "https://schema.org/NewCondition",
    "url": {{ shop.url | append: product.url | json }},
    "priceValidUntil": "{{ "now" | date: "%Y" | plus: 1 }}-12-31",
    "seller": {
      "@type": "Organization",
      "name": {{ shop.name | json }}
    }
  }
  {% if product.metafields.reviews.rating != blank %}
  ,
  "aggregateRating": {
    "@type": "AggregateRating",
    "ratingValue": {{ product.metafields.reviews.rating.value.rating | json }},
    "reviewCount": {{ product.metafields.reviews.rating_count | json }},
    "bestRating": "5",
    "worstRating": "1"
  }
  {% endif %}
}
</script>

Shopify stores prices as integers in pence or cents. The divided_by: 100.0 conversion is mandatory — the .0 forces floating-point division. Pipe every dynamic value through | json. Theme updates will reinstate {{ product | structured_data }}; treat schema snippets as part of your theme version control, not a one-off paste.

WooCommerce and WordPress

Yoast WooCommerce SEO and Rank Math can output Product schema, but you still need to verify brand, GTIN, and review markup against the Rich Results Test. If the plugin block is incomplete, add JSON-LD in functions.php or a small custom plugin, and disable the plugin's Product schema to avoid duplicate Product blocks. Generate schema from $product->get_price(), stock status, and your reviews plugin's rating fields — not from a static string.

BigCommerce, Magento, Webflow, and custom storefronts

BigCommerce Stencil themes and Magento 2 often emit basic Product markup that still misses reviews or identifiers. Webflow and headless builds usually need a JSON-LD embed in the page head, populated from CMS fields. Same rules: one Product block, decimal prices, absolute image URLs, ratings only when reviews exist.

Collection and category page schema: ItemList

Category pages are one of the most overlooked schema opportunities in ecommerce SEO. A category page is a curated list of products — which maps directly to Schema.org's ItemList type. Adding this signals to Google the page's purpose, its constituent products, and the relationship between them.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "ItemList",
  "name": "Running Socks",
  "description": "Moisture-wicking running socks for training and race day.",
  "url": "https://www.yourstore.com/collections/running-socks",
  "numberOfItems": 24,
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "url": "https://www.yourstore.com/products/merino-wool-running-socks",
      "name": "Merino Wool Running Socks — 3-Pack"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "url": "https://www.yourstore.com/products/compression-running-socks",
      "name": "Compression Running Socks"
    }
  ]
}
</script>

Limit the list to the first 20 or so products. Outputting every SKU in a large catalogue produces a very large JSON-LD block — which slows page load and provides diminishing signal value. Google does not need 200 products listed in your schema to understand what the category is about.

The category description field deserves attention here. Most stores leave category descriptions blank — which means the description property outputs an empty string. Google uses these descriptions as a primary input for understanding what the collection is about and which search queries it should serve. Writing a focused, keyword-aware category description (100–300 words) is one of the highest-ROI content actions for collection page SEO — and it directly improves your schema output.

On Shopify, save this as snippets/schema-collection.liquid and loop collection.products with {% unless forloop.last %},{% endunless %} so the JSON stays valid. On WooCommerce, build the same ItemList from the current product query on archive templates.

If your store has a meaningful hierarchy — products nested within collections nested within categories — BreadcrumbList schema is the third piece of the puzzle. It tells Google the navigational path to any given page, which it uses to generate breadcrumb-style rich results (the "Home > Running Shoes > Trail" path that sometimes appears under your title in search results).

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "Home",
      "item": "https://www.yourstore.com"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "Running",
      "item": "https://www.yourstore.com/collections/running"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "Socks",
      "item": "https://www.yourstore.com/collections/running-socks"
    }
  ]
}
</script>

The breadcrumb trail in schema must match the visible breadcrumbs on the page. Do not invent a cleaner hierarchy in JSON-LD than shoppers actually see.

How to validate your schema

Writing the snippet is only half the work. Schema that appears valid in your code can still fail Google's validation for reasons that are not always obvious — malformed JSON from a product description containing special characters, a blank required field, or a template filter producing an unexpected output type.

Use these tools in sequence after deploying any schema changes:

  • Google Rich Results Test (search.google.com/test/rich-results) — paste your product page URL and Google will fetch the live page, parse all structured data blocks, and tell you exactly which rich result types you are eligible for and which errors or warnings are present. This is the authoritative source.

  • Schema.org Validator (validator.schema.org) — useful for checking the structural validity of your JSON-LD independently from Google's rich results requirements. Catches JSON syntax errors that the Rich Results Test sometimes surfaces less clearly.

  • Google Search Console → Enhancements — once your schema is deployed and Googlebot has crawled your pages, the Enhancements section in Search Console shows rich result coverage, errors, and warnings across your entire site. Monitor this after deployment and again after any theme, plugin, or template updates.

One common failure mode worth calling out explicitly: special characters in product descriptions. If a description contains quotation marks, ampersands, or non-UTF-8 characters, they must be JSON-escaped. Any concatenated string in your schema that is not escaped is a potential JSON syntax break. The rule is simple: every dynamic variable in your JSON-LD must go through a JSON encoder.

Theme and plugin updates will break your custom schema — here's what to do

This is the single most common reason well-implemented product schema stops working: a theme or plugin update overwrites the product template, reinstates the platform default, and you are back to incomplete markup without realising it.

Make schema part of your release process, not a one-off addition. Keep snippet files in version control, document that they must be re-included after any theme update, and add a periodic Rich Results Test check as a standing SEO audit task. Search Console Enhancements will also alert you if coverage drops — but only after Googlebot has recrawled the affected pages, which introduces a lag.

If you cannot own the theme, a dedicated schema app or plugin that persists across updates is a reasonable tradeoff: less granular control, weighed against zero risk of losing your schema on update. Still validate the output. Most apps emit a good but not perfect implementation.

Common schema mistakes on ecommerce stores — and how to fix them

Mistake

The problem

The fix

Platform default and a custom snippet together

Two Product schema blocks on the same page — Google flags conflicting structured data and may ignore both

Keep one Product block. Remove the theme filter, plugin default, or app injection before deploying your custom snippet.

Price output as an integer (8999 instead of 89.99)

Fails Google's rich results price validation — listing excluded from price-featured results

Always emit a decimal in the listed currency. On Shopify, divided_by: 100.0 — the .0 is not optional.

Protocol-relative image URLs (//cdn.example.com/...)

Google's schema validator rejects URLs without explicit protocol — images not validated

Use absolute https:// URLs for every image.

Description output without stripping HTML

Raw HTML tags in JSON-LD produce malformed JSON — entire schema block rejected

Always strip HTML, then JSON-encode the description.

AggregateRating block present with zero reviews

A ratingValue with no actual reviews is a spam signal — Google may suppress rich results

Only output aggregateRating when reviews exist and review count is greater than zero.

Missing priceValidUntil

Price rich results eligibility reduced — Google may not display price in the listing

Add a future priceValidUntil. Never use a past date.

Category description left blank

Schema outputs empty description string — missed signal for category topic and keyword relevance

Write 100–300 words of category description for every collection you want to rank.

No GTIN despite products having barcodes

Missed opportunity to cross-reference against Google's product database

Map barcode / GTIN fields to gtin. If products don't have barcodes, omit — do not fabricate GTINs.

Schema not re-checked after theme or plugin updates

Update reinstates default markup or breaks conditionals — schema silently reverts or breaks

Monitor Search Console Enhancements after every theme or plugin update. Add schema check to your post-update QA checklist.

Trailing comma in ItemList JSON

The last item in the loop outputs a trailing comma — invalid JSON, entire block rejected

Do not emit a comma after the final list item.

Schema doesn't match visible content

Manual action risk. Loss of rich result eligibility.

Every schema property must reflect what is actually visible on the page — especially price, availability, and ratings.

Quick reference: which schema type for which ecommerce page

Page type

Schema type

Rich result unlocked

Priority

Product page

Product + Offer

Price, availability in listing

Critical — implement first

Product page with reviews

Product + AggregateRating

Star ratings in listing

Critical — highest CTR impact

Category / collection page

ItemList

Product carousel / sitelinks

High

Product or category page

BreadcrumbList

Breadcrumb path in listing

Medium

Blog post / guide

Article

Article rich result, date

Medium

Homepage

Organization + WebSite

Sitelinks search box

Low-medium

FAQ page or product FAQ section

FAQPage

Expandable FAQ in listing

Situational

Based on "The Science of Google Search: A Complete SEO Dissertation" and "The Complete SEO System" — grounded in verified Google patents and Schema.org specifications. JSON-LD examples tested against Google's Rich Results Test and Search Console Enhancements reporting.

Ready to put this into practice?

Sharkly handles your keyword research, content strategy, and article generation — automatically.

Try Sharkly Now