Skip to content

How do I add product schema on Shopify?

By cartscaling Reviewed 5 min read

The short answer

Check the theme first. Most modern Shopify themes already output product structured data, and the common problem is not that it is missing but that an app has added a second copy. If it genuinely is missing, add one block in the product template that reads price, currency and availability from the live objects.

Find out what the theme already outputs before you add anything

Most current Shopify themes include product structured data as standard, so the first job is to look rather than to install. Adding a second block on top of an existing one causes more problems than having none.

Open a product page, view the page source, and search it for the script block that carries structured data. You are looking for an entry describing a product, with a name, an offer and a price. If you find one, check whether there are two, which happens when a theme and a review app or an SEO app each add their own.

Then run a validator against the page and read what it reports rather than what you assume. A theme block that exists but is missing the brand, or is reporting a price that does not update when the variant changes, is a small fix. A missing block is a bigger job.

Do this on three products: a simple one, one with variants at different prices, and one that is out of stock. Those three cover most of the situations where a theme's markup falls over, and checking them takes a few minutes rather than the afternoon you would spend rewriting something that was already working.

Where to put it when it is genuinely missing

Add the markup in the product template, or in a snippet that the product template renders, rather than in a global layout file. That keeps it scoped to product pages and makes it easy to find later.

The practical route is to create a snippet containing the structured data, then render it from the product template. Doing it that way means the block lives in one file, so the next person can find it, and it will not accidentally output on pages that are not products.

Write it as a JSON script block rather than scattering attributes through the markup. It is easier to read, easier to validate, and much easier to keep in step with the page when the template changes.

Avoid two shortcuts that cause problems later. Do not paste a static block with hard-coded values, because the moment a price changes the markup is wrong. And be cautious about installing an app purely for this, because apps that inject structured data cannot see what your theme already outputs and frequently create the duplicate problem.

If you are not comfortable editing theme files, duplicate the theme first and work on the copy. Publishing a theme change on a live store without a rollback is an avoidable risk.

Read price, currency and availability from the live objects

The values have to come from the same place the page gets them, or the markup will drift out of step within a week.

Price should come from the currently selected or first available variant rather than a fixed value, and it needs converting from the whole-number format the platform stores it in. Getting this wrong produces prices that are a hundred times too large, which is a common and very visible error.

Currency should come from the storefront's active currency rather than being hard-coded, otherwise a store selling in several markets will report the same currency to everyone.

Availability should be derived from whether the variant can actually be bought, so it changes automatically when stock runs out. A page saying sold out while the markup says in stock is a mismatch that affects both search display and shopping surfaces.

The URL in the offer should be the canonical product URL, not whichever collection path the visitor arrived through.

For products with several variants at different prices, decide whether to present a single price or a range, and make that consistent with what the page shows. A page displaying a from price with markup claiming a single fixed price is the sort of small inconsistency that quietly removes a rich result.

Identifiers, brand and ratings in a Shopify catalogue

Three fields need data that lives in specific places in a Shopify catalogue, and they are usually the ones missing.

The brand normally comes from the product vendor field. Stores that use the vendor field for a supplier name rather than the brand will output the wrong thing, so check what yours actually contains before wiring it up.

The trade item number lives in the barcode field on each variant. It is often empty because nobody filled it in during import, and it is worth populating, because identifiers help both search and shopping surfaces match your product to the same product elsewhere. For products you make yourself with no such number, leave the field out rather than inventing a value.

The part number can come from the SKU where your SKUs are the manufacturer's codes. Where they are your own internal codes, they are less useful and a metafield holding the real part number is the better source.

Ratings are the one to be careful with. If a review app injects its own rating markup, do not add another. And never output a rating for a product with no reviews. An empty or invented rating risks structured data being ignored across the whole store, which costs more than the stars were worth.

Test it, then test it again after every app and theme change

Validate several product pages after the change, and put a reminder in the calendar to check again periodically, because this breaks silently.

The test set that catches the most: a simple single-variant product, a product with variants at different prices, a product that is out of stock, a product on sale, and if you sell internationally, the same product on a second market. Compare each field in the markup against what the page displays.

The events that break it are predictable. A theme update overwrites a customised file. An app is installed that adds its own block. A review platform is switched. A market is added and the currency is wrong on the new one. A product template is duplicated for a new section and the snippet is not rendered on the copy.

After fixing anything, ask for the pages to be recrawled rather than waiting, and then watch your search console reports for product issues over the following weeks.

One last check worth doing. Look for pages that are not products but are outputting product markup, which happens when a block is placed in a global layout file. That is the sort of error that gets an entire site's structured data discounted.

Worked example, not a result

A check across five product pages

Simple product
One product block, price correct
Multi-variant product
Price range matches the page
Out of stock product
Availability matches the buy button
Sale product
Sale price, not the compare-at price
Second market
Currency follows the storefront
Duplicate blocks found
1, from a review app
Barcode field populated
On 320 of 420 variants

Worked example, not a result

Related questions

Most current themes do, and older or heavily customised themes often do not. Check the page source on a product page before assuming either way, because adding a second block when one already exists is the more common problem.

Only if your theme genuinely outputs nothing and editing the template is not an option. Apps cannot see what the theme already produces, so they frequently create duplicate blocks that conflict with each other.

Usually because the price is being read as the raw stored value without conversion, or because it is taken from the first variant rather than the selected one. Check a product where variants have different prices, since that is where the error shows.

Not on its own. Merchant Center compares your feed against the page, and structured data helps it read the page correctly, but the underlying fix is making the feed price, the markup price and the displayed price all agree.

Keep reading

Want this answered for your store?

Send it. The plan looks at your numbers, not the general case.

Store, name, email and phone. A person replies from a real inbox. Privacy

Get your free growth plan