SEO
Product schema for e-commerce, with the right price
Product schema is a JSON-LD block that states the name, image, price and stock state of a product page. Add it in the page and keep the price identical.
Key points
- Product schema describes a product page with @type Product: name, image and an offer with price and currency.
- Google separates Product snippets, for pages without a purchase, from Merchant listings, for pages where people buy.
- A price in the markup that differs from the visible price is a semantic mismatch no validator reports.
Product schema is a JSON-LD block with @type Product that states the name, image, price and stock state of a product page. It goes inside the page, not in the feed, and it must repeat what the visitor sees.
Two types, not one
Google documents two sets of requirements. Product snippets covers pages where nobody buys, such as a review, and asks for name, image and at least one of offers, review and aggregateRating. Merchant listings covers pages where people buy and adds the selling details: shipping and return policy. Source: the Product structured data page of Google Search Central, read on 1 November 2026.
The minimum block
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Camicia in lino",
"image": ["https://example.com/img/camicia-lino.jpg"],
"sku": "CAM-LIN-01",
"brand": { "@type": "Brand", "name": "Example" },
"offers": {
"@type": "Offer",
"url": "https://example.com/prodotti/camicia-lino",
"price": "79.00",
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock"
}
}
The fields that decide the outcome: name matching the visible title, image as an absolute URL, offers.price as a string without a currency symbol, offers.priceCurrency in ISO 4217 and offers.availability as a schema.org URL, for example https://schema.org/InStock. sku, gtin13, mpn and brand identify the product when a catalogue exists; without identifiers Google compares text and price alone.
The price has to match
A price in the markup that differs from the price on the page is a discrepancy, not a syntax error: no validator sees it, and Google can apply a manual action. The common case is the variant page, where a size selector changes the price with JavaScript while the JSON-LD keeps the base value. When variants are real, the correct markup is ProductGroup with hasVariant, not ten unlinked Product blocks.
Checking before deployment
cat > check-product.mjs s.slice(s.indexOf(">") + 1, s.indexOf("")));
const blocks = raw.map((s) => JSON.parse(s)).filter((j) => j["@type"] === "Product");
for (const p of blocks) {
const o = p.offers || {};
console.log(p.name, o.price, o.priceCurrency, o.availability);
}
console.log(`${blocks.length} Product blocks`);
EOF
curl -s https://ui.eneawork.it/blocks/creativetimofficial-ui/product-details-01 > page.html
node check-product.mjs page.html
The script splits every JSON-LD block, keeps the Product ones and prints name, price, currency and availability. On the product-details-01 block it prints 0 Product blocks: a layout block holds the place for the price, not the price. Your data layer supplies the value, and the script tells you at once when it is missing.
The EneUI catalogue
For a product page the records to inspect are commercn product-detail-01, Ravikatiyar product card and the Kokonut UI interactive checkout, MIT licence. The pricing category holds 41 blocks for the commercial side. None of these records states a price: they are structure, and Product markup is written on top from real data. Checked on 1 November 2026.
The limitation
Product schema does not build a full merchant listing. For the rich card with shipping, returns and sellers Google also reads the Merchant Center feed, and without identifiers the result stays thin. On a catalogue with variants, a page covering ten of them needs ProductGroup: one Product per variant, all linked, or the page claims a product that does not exist.
Where to continue
The general shape of the markup is in FAQPage schema step by step; the blocks for a shop are in blocks for e-commerce; the sequence for opening the shop is in building an e-commerce site.
Frequently asked questions
Is Product schema required?
No. It is an optional addition that helps Google understand the page and can produce a rich result. Without markup the page stays valid, but it loses the price and availability details in the result.
Where does the price go?
In offers.price, as a string without a symbol, with offers.priceCurrency in ISO 4217. The value must match the visible price and the price at checkout.
Do I need a Merchant Center feed?
For Product snippets, no: the JSON-LD on the page is enough. For full merchant listings, with shipping and returns, Google also reads the feed, and the two must agree.
Can I mark several products on one page?
Yes. Use one block per product, or an ItemList that lists them, as long as every Product has its own URL and price. Do not repeat the same product with different data.
What if the price changes often?
Update the markup in the same request that updates the visible price, and update the page updatedAt. A stale price in JSON-LD is the most common cause of a manual action on a shop.