Product JSON-LD Schema in Next.js: A Practical Guide for Stores
By Arash Latifi
Implement schema.org Product markup with JSON-LD in the Next.js App Router, with full code, common template mistakes, and a pre-publish validation checklist.
TL;DR — Product schema is a JSON block in your page head that tells Google this is a product with a specific price and stock state; in Next.js you build it with one small server component.
No matter how good your product page copy is, Google is guessing your price and stock state from the visible price text. It guesses wrong often, because the same product can show different prices on other pages.
JSON-LD is the direct way to tell Google otherwise. The payoff is eligible rich results and clearer listings.
Before the code: separate data from markup
The rule that keeps things clean: never mix HTML into the schema. JSON-LD is a <script type="application/ld+json"> block that adds nothing to the page and never shows up in the rendered content.
Define price and stock first. For Iranian stores, currency naming differs from what you would use internationally, so set it explicitly:
"priceCurrency": "IRR"
Now the implementation. The fields that matter:
| Field | Why it matters | Typical source in your app |
|---|---|---|
| name | Headline shown in results | Product title |
| description | Snippet shown next to price | Product summary |
| sku | Ties the price to a specific product | id or SKU code |
| image | Thumbnail in search results | CDN image URL |
| offers.price | Live price, not a stale discounted one | Active price from the database |
| offers.availability | Stock state used in results | stock greater than zero |
Full code: a product schema component
One small server component is enough in the App Router. The key detail: no use client directive, so the output lands directly in the final HTML with no client-side JavaScript required.
Then use it on the product page:
Why this pattern holds up
Three things usually break it:
- Keep it a server component. Adding
use clientsends the data into the JavaScript bundle, where it may arrive late or be missing from the initial HTML entirely. - Use
JSON.stringify. Hand-writing JSON invites missing commas. - Pass price as a string.
schema.orgexpects text, not a float.
If one page lists many products, you can group them under a single array with @graph. For the rest of your metadata setup, see the metadata and OG guidenextjs-metadata-og-sitemap-seo-tech.
Mistakes common in shop templates
Schema on two products, none on the rest. Markup has to be on every product page, or you get an inconsistent pattern in Search Console.
Displayed price differs from the real price. If you run discounts, price must match what the customer actually pays, and discounts belong in an AggregateOffer.
Images live on another domain. The image URL must be directly fetchable. See the image optimization guide for sizing and storage paths.
Correct structure, thin content. Schema does not replace useful copy. Pair it with the product page guideproduct-page-that-sells.
Pre-publish validation
- [ ] Inspect the output with
curl -s https://example.com/product/123 | grep application/ld.json - [ ] Run that output through the Schema Markup Validator
- [ ] Test the
Producttype in Rich Results Test - [ ] Confirm the price matches what the page shows
- [ ] Test on mobile that the schema is present in the first render
- [ ] Validate five different products, not just one
- [ ] Clear the cache, then recheck Search Console after a few days
What happens after it goes live
The Enhancements report in Search Console starts filling up with schema errors. You do not have to fix them all at once. Start with the error type that has the highest count, usually price and availability, then work down the list.
Schema does not move rankings by itself. What it does is make your data unambiguous, so Google can display it confidently and users click with better information.
FAQ
Q: What is the difference between JSON-LD and Microdata?
JSON-LD lives in a separate script block and adds no markup to your HTML; Microdata sits inside HTML attributes and is harder to maintain. Google supports both, but JSON-LD is cleaner.
Q: Do I need separate schema code for every product?
Write one shared component and pass each product's data to it. Copy-pasting markup per page creates maintenance debt.
Q: Does a schema score affect rankings?
Not directly. Rich results with price and availability do improve click-through rate, which matters for ranking.