Chat on WhatsApp

Structured Data JSON-LD for Magento 2

One JSON-LD block per page, product-type aware, theme-agnostic. Panth Structured Data emits a single with a deduplicated @graph covering every schema.org node Google cares about — Product, Offer / Aggr…

Magento 2.4.6–2.4.8 PHP 8.1–8.4 Hyva + Luma Ready Free

Key Features:

  • One Block, One Graph, Zero Duplicates
  • Full Product Type Coverage
  • Identity Nodes
  • Content-Rich Nodes

Additional Services

$0.00
In stock
SKU
panth-structured-data
Links
Pay with Wise
Lifetime Updates Every Magento release
1-Year Free Support Email + WhatsApp
Adobe-Certified Magento 2 Developer
Free Forever No subscription, no upsell
What you get

Everything in the box

Built-in from day one. No add-ons, no upsell, no licence keys to renew.

One Block, One Graph, Zero Duplicates

Own blocks are explicitly skipped — scripts carrying data-panth-seo are preserved.

Full Product Type Coverage

Simple → Product + scalar Offer (price / availability / itemCondition / seller / priceValidUntil / shippingDetails).

Identity Nodes

Organization — name / url / logo / legalName / contactPoint (phone + email) / PostalAddress / sameAs array merging the 7 dedicated social…

Content-Rich Nodes

BreadcrumbList — full category hierarchy for products (Home → Gear → Bags → Product), not just Home + Product. Respects admin-configured…

Merchant / Pricing Extras

MerchantReturnPolicy — applicableCountry (ISO 3166-1), merchantReturnDays (int), returnMethod, returnFees (properly mapped to the schema.…

Specialist Nodes

Brand — standalone Brand node using the configured product attribute (default manufacturer) + admin-configurable default fallback brand.

Overview

Panth Structured Data is a JSON-LD schema markup extension for Magento 2 and Adobe Commerce that emits one validated rich-result graph per page.

The module replaces the scattered structured data Magento ships natively with a single deduplicated application/ld+json block per URL. An Aggregator deep-merges every contributor (Product, AggregateOffer, BreadcrumbList, Organization, WebSite, FAQPage) by @id, then a native-markup strip plugin removes the duplicate JSON-LD that Magento's product, breadcrumb, and price blocks otherwise inject — the source of most Google Search Console "duplicate field" errors. Existing blocks carrying data-panth-seo are preserved automatically.

The module covers all six Magento product types — simple, configurable, bundle, grouped, virtual, downloadable — with correct AggregateOffer lowPrice / highPrice math and per-variant child offers carrying their own SKU, price, and availability. Identity nodes pull from existing Magento config (logo, legal name, postal address, contact phone, the seven social-profile fields), so going from install to a clean Rich Results Test pass typically takes under 15 minutes on a stock catalog.

Specialist nodes — FAQPage, WebSite SearchAction for Sitelinks Search Box, optional ProductGroup with hasVariant and variesBy — ship behind admin toggles, so each rich-result type is opt-in instead of forced on.

  • Stores chasing Product, FAQ, and Breadcrumb rich snippets in Google
  • Hyvä or Luma catalogs with configurable / bundle / grouped SKUs
  • Sites with existing Google Search Console "duplicate JSON-LD" warnings

What you get

Schema.org JSON-LD emission for every Magento 2 page type, deduplicated by @id and validated against Google Rich Results.

  • One application/ld+json block per page — native Magento markup auto-stripped
  • Product, AggregateOffer, BreadcrumbList, Organization, WebSite, FAQPage nodes covered
  • Configurable / bundle / grouped offers with lowPrice, highPrice, offerCount math
  • WebSite SearchAction for Sitelinks Search Box eligibility
  • Optional ProductGroup + hasVariant modeling for color / size / material variants
  • Works on Hyvä and Luma without template overrides

Admin Preview

Stores → Configuration → Panth Extensions → Structured Data carries the full configuration surface — Social Profiles (Organization sameAs), Organization Details, Structured Data toggles (JSON-LD), and Breadcrumbs. Every field is [store view]-scoped so a multi-store install can ship a different schema per storefront.

Admin configuration


Key Features

One Block, One Graph, Zero Duplicates

  • Single <script type="application/ld+json" data-panth-seo="jsonld"> per page. Everything the module knows about the current entity lives inside one @graph.
  • Aggregator deduplicates by @id and deep-merges so the Product node emitted by ProductProvider and the AggregateOffer from ConfigurableOfferProvider become one coherent Product with one Offer, no collisions.
  • Native-markup strip plugin removes Magento's built-in application/ld+json from product.info.main, breadcrumbs, product.price.final plus all itemprop / itemscope / itemtype microdata attributes on those blocks — so you never ship two Product schemas.
  • Own blocks are explicitly skipped — scripts carrying data-panth-seo are preserved.

Full Product Type Coverage

  • SimpleProduct + scalar Offer (price / availability / itemCondition / seller / priceValidUntil / shippingDetails).
  • ConfigurableProduct + AggregateOffer (lowPrice / highPrice / offerCount) with one child Offer per visible variant, each carrying its own price / sku / availability / url.
  • BundleProduct + AggregateOffer with lowPrice / highPrice pulled from the bundle option price range. Fixed-price bundles get a single Offer with the fixed final price.
  • GroupedProduct + AggregateOffer with one child Offer per grouped SKU (name / price / availability / sku / url).
  • Virtual / Downloadable → treated like simple products — Product + Offer.
  • ProductGroup + hasVariant (optional, opt-in) → richer variant modelling via ProductGroup + variesBy on color / size / material.

Identity Nodes

  • Organization — name / url / logo / legalName / contactPoint (phone + email) / PostalAddress / sameAs array merging the 7 dedicated social-profile fields (Facebook, Twitter/X, Instagram, LinkedIn, YouTube, Pinterest, TikTok) with a free-form textarea for anything else.
  • WebSite — with SearchAction for Google Sitelinks Search Box eligibility.
  • Seller — auto-merged into #organization by default (since business_type = Organization); switches the @type to LocalBusiness / Store / OnlineStore when configured.

Content-Rich Nodes

  • BreadcrumbList — full category hierarchy for products (Home → Gear → Bags → Product), not just Home + Product. Respects admin-configured category priority weights when enabled.
  • ItemList — on category pages, listing products with position for rich carousel results.
  • Review + AggregateRating — top-N approved reviews with ISO 8601 datePublished, scaled 1-5 rating from Magento's 0-100 percent votes.
  • FAQPage — auto-extracted Question / acceptedAnswer pairs from product / category / CMS descriptions. Recognises <h2>…?</h2><p>…</p> and <h3>…?</h3><p>…</p> patterns.
  • Article — for CMS pages under blog/*, news/*, articles/* or carrying article in their meta keywords.
  • VideoObject — for product media gallery entries of type external-video (YouTube / Vimeo).

Merchant / Pricing Extras

  • MerchantReturnPolicyapplicableCountry (ISO 3166-1), merchantReturnDays (int), returnMethod, returnFees (properly mapped to the schema.org ReturnFeesEnumeration URL).
  • Product pricing: priceValidUntil from product special_to_date → admin default → auto +1 year fallback; availability from isSalable() / StockRegistry with LimitedAvailability / BackOrder / PreOrder / Discontinued beyond InStock / OutOfStock.
  • Sale Event (opt-in) — priceSpecification with validFrom / validThrough on products with active special prices.
  • Delivery Methods — structured shipping details from admin-listed methods.
  • Payment MethodsacceptedPaymentMethod in Offer from admin list.
  • Multi-Region Shipping (auto-detect) — reads Magento's table-rate / flat-rate config and emits shippingDetails per region. Only when admin hasn't configured manual delivery_methods.

Specialist Nodes

  • Brand — standalone Brand node using the configured product attribute (default manufacturer) + admin-configurable default fallback brand.
  • CertificationshasCertification from a textarea attribute in Authority | Name | ID format.
  • Energy Efficiency Label (EU)hasEnergyConsumptionDetails with grades A–G.
  • Pros / ConspositiveNotes / negativeNotes ItemLists from textarea attributes.
  • Custom Properties — admin-configurable JSON object deep-merged into the Product node for anything beyond the built-in schemas (e.g. material, audience, custom-vocabulary facets).

Production-Grade Implementation

  • Theme-agnostic — head block attached to head.additional via view/frontend/layout/default.xml. No JS, no RequireJS, no x-magento-init. Identical output on Hyva, Luma, Breeze, and every custom theme.
  • Fully cacheable — the head block is cacheable="true", so FPC bakes the JSON-LD alongside the rest of the <head> and the providers only run on uncached renders.
  • ISO 8601 dates everywhere — product dateModified / datePublished, review datePublished, article dates, sale validFrom/validThrough all go through DateTimeImmutable::format(ATOM) for strict schema.org compliance.
  • Attribute-text coercedgetAttributeText() return values (string | array | false | null from multi-select attributes) are normalised before use, preventing a single multi-select attribute from silently killing the entire Product node.
  • XSS-safe JSON payload — every </ inside the JSON is escaped to <\/ so browsers can't be tricked into closing the <script> tag early.

How It Works

The module is a pipeline: admin config → providers → aggregator → head block → <script> tag.

┌─────────────────────────────────────────────────────────────────────┐
│ Admin → Stores → Configuration → Panth Extensions → Structured Data│
│ (toggles, attribute codes, social URLs, organization fields) │
└────────────────────────────────────┬────────────────────────────────┘
 │
 Helper\Config reads every field
 ▼
┌─────────────────────────────────────────────────────────────────────┐
│ Aggregator (di.xml-registered pipeline of 24 providers) │
│ │
│ OrganizationProvider → Organization #organization │
│ WebsiteProvider → WebSite #website (+ SearchAction) │
│ BreadcrumbProvider → BreadcrumbList │
│ ProductProvider → Product #product + scalar Offer extras │
│ FaqExtractor → FAQPage (if Q&A pairs detected) │
│ ReviewProvider → Review[] (top 5 approved) │
│ VideoProvider → VideoObject[] (external-video entries) │
│ CmsArticleProvider → Article (blog-like CMS pages) │
│ ReturnPolicyProvider → MerchantReturnPolicy │
│ BrandProvider → Brand │
│ ProductListProvider → ItemList (category pages) │
│ PaymentMethodProvider → Offer (acceptedPaymentMethod) │
│ DeliveryMethodProvider→ Offer (shippingDetails) │
│ MultiRegionShipping → Offer (multi-region shippingDetails) │
│ ConfigurableOffer → Product #product → AggregateOffer │
│ GroupedOffer → Product #product → AggregateOffer (group) │
│ BundleOffer → Product #product → AggregateOffer (bundle) │
│ CustomProperties → Product #product + merchant-defined JSON │
│ EnergyLabel → Product #product + hasEnergyConsumption │
│ Certification → Product #product + hasCertification │
│ SaleEvent → Product #product + priceSpecification │
│ ProductGroup → ProductGroup + hasVariant │
│ ProsCons → Product #product + positiveNotes/negative │
│ SellerProvider → Organization #organization (merged) │
└────────────────────────────────────┬────────────────────────────────┘
 │
 Deep-merge by @id, deduplicate, validate
 ▼
┌─────────────────────────────────────────────────────────────────────┐
│ {"@context":"https://schema.org","@graph":[ ...merged nodes... ]} │
│ JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE + `</` → `<\/` │
└────────────────────────────────────┬────────────────────────────────┘
 │
 Block\Head\StructuredData echoes inside:
 ▼
┌─────────────────────────────────────────────────────────────────────┐
│ <script type="application/ld+json" data-panth-seo="jsonld"> │
│ { ...full @graph... } │
│ </script> │
│ │
│ RemoveNativeMarkupPlugin also runs on AbstractBlock::afterToHtml │
│ and strips Magento's native ld+json + microdata from three blocks: │
│ product.info.main, breadcrumbs, product.price.final │
│ (own blocks carrying data-panth-seo are explicitly preserved) │
└─────────────────────────────────────────────────────────────────────┘

Six things the Aggregator does

  1. Applicability check — every provider exposes isApplicable(). Product-scoped providers return false on CMS pages, ReturnPolicyProvider returns false when return_policy_days = 0, FaqExtractor defers to Panth_Faq when that module owns the current page. Providers that return false are skipped entirely.
  2. Config gateConfig::isStructuredDataEnabled($provider->getCode()) reads panth_structured_data/structured_data/{code}. Admin disables any provider with a single Yes/No toggle.
  3. Node collection — applicable + enabled providers' getJsonLd() returns are collected. Single-node returns and list-of-nodes returns both work.
  4. Dedup + deep-merge — two nodes with the same @id get merged key-by-key. Scalars from later providers win, arrays recurse, new keys get appended. Two providers contributing to the same Product, Organization, or Offer always produce one coherent node.
  5. Document shape — one node in @graph → inline the node under @context. Multiple nodes → wrap in @graph.
  6. XSS safety — every </ in the serialised JSON is replaced with <\/ so the <script> tag can never be closed early by a malicious product name.

The deep-merge, concretely

Before merge (two separate provider outputs on a configurable PDP):

// ProductProvider
{
 "@type": "Product",
 "@id": "https://example.com/hoodie.html#product",
 "name": "Chaz Kangeroo Hoodie",
 "offers": {
 "@type": "Offer",
 "itemCondition": "https://schema.org/NewCondition",
 "seller": {"@id": "https://example.com/#organization"},
 "priceValidUntil": "2027-04-23",
 "shippingDetails": { ... }
 }
}

// ConfigurableOfferProvider
{
 "@type": "Product",
 "@id": "https://example.com/hoodie.html#product",
 "offers": {
 "@type": "AggregateOffer",
 "lowPrice": "52.00",
 "highPrice": "52.00",
 "offerCount": 15,
 "priceCurrency": "USD",
 "offers": [ {...15 child offers...} ]
 }
}

After merge in @graph:

{
 "@type": "Product",
 "@id": "https://example.com/hoodie.html#product",
 "name": "Chaz Kangeroo Hoodie",
 "offers": {
 "@type": "AggregateOffer",
 "lowPrice": "52.00",
 "highPrice": "52.00",
 "offerCount": 15,
 "priceCurrency": "USD",
 "itemCondition": "https://schema.org/NewCondition",
 "seller": {"@id": "https://example.com/#organization"},
 "priceValidUntil": "2027-04-23",
 "shippingDetails": { ... },
 "offers": [ {...15 child offers...} ]
 }
}

Scalar @type is overridden by the later ConfigurableOfferProvider ("AggregateOffer" wins). Every scalar key unique to one side is preserved. shippingDetails (nested array) comes through untouched from ProductProvider. offers (the child-offer array from ConfigurableOfferProvider) is added without conflict.


Schema.org Nodes Emitted

Node @id pattern When
Organization {store_url}#organization Every page
WebSite {store_url}#website Every page (toggle)
BreadcrumbList {store_url}#breadcrumb-{sha1 of path} Product, category, CMS (2+ items)
Product {product_url}#product Product pages
Offer / AggregateOffer inline under Product.offers Product pages (shape depends on type)
Review no @id (list items) Product pages with approved reviews
AggregateRating inline under Product.aggregateRating Product pages with votes
MerchantReturnPolicy no @id Product pages when return_policy_days > 0
FAQPage {store_url}#faq-{sha1 of path} Any page with ≥2 Q&A pairs in description
Article {page_url}#article Blog-like CMS pages
VideoObject no @id (list items) Product pages with external-video gallery entries
ItemList {category_url}#productlist Category pages with products
Brand inline under Product.brand Product pages when brand attribute has value
ProductGroup {parent_url}#productgroup Configurable products (opt-in)

All URL-valued attributes (og:url, url, image, logo, sameAs) preserve : and / (no entity-encoding). All dates are ISO 8601 (YYYY-MM-DDTHH:MM:SS+00:00). All enumerations (availability, itemCondition, returnFees, returnPolicyCategory, returnMethod) are full schema.org URLs.


Product Type Coverage

Tested end-to-end across Magento's sample catalogue on both Hyva and Luma:

Type Sample SKU Sample URL Product.offers.@type Notes
Simple 24-WB02 /compete-track-tote.html Offer price, availability, itemCondition, seller, priceValidUntil, shippingDetails
Configurable MH01 /chaz-kangeroo-hoodie.html AggregateOffer 15 child Offers (one per variant), lowPrice / highPrice / offerCount
Bundle (dynamic) 24-WG080 /sprite-yoga-companion-kit.html AggregateOffer lowPrice / highPrice from bundle option range, offerCount = 1
Bundle (fixed) any fixed-price bundle Offer single Offer with fixed final price
Grouped 24-WG085_Group /set-of-sprite-yoga-straps.html AggregateOffer 3 child Offers (one per grouped SKU)
Virtual any virtual product Offer same as simple
Downloadable any downloadable product Offer same as simple

Across all variant types the shared offer-level extras (itemCondition, seller @id, priceValidUntil, shippingDetails) merge into the AggregateOffer via the deep-merge path, so a bundle / grouped / configurable product still carries its return policy and product condition on the Offer node.


Quick Test — URLs You Can Hit Right Now

Every URL below returns HTML with exactly one <script type="application/ld+json" data-panth-seo="jsonld">…</script> block. Substitute your own host for the domain.

1. View the JSON-LD in a browser

Open any URL → right-click → View Page Source → Ctrl-F for application/ld+json. The block carries the full @graph.

Product types — Hyva + Luma test stores

Type Hyva Luma
Simple https://your-store.test/compete-track-tote.html https://luma.your-store.test/compete-track-tote.html
Configurable https://your-store.test/chaz-kangeroo-hoodie.html https://luma.your-store.test/chaz-kangeroo-hoodie.html
Bundle https://your-store.test/sprite-yoga-companion-kit.html https://luma.your-store.test/sprite-yoga-companion-kit.html
Grouped https://your-store.test/set-of-sprite-yoga-straps.html https://luma.your-store.test/set-of-sprite-yoga-straps.html

Other page types

Page Hyva Luma
Home https://your-store.test/ https://luma.your-store.test/
Category (ItemList) https://your-store.test/gear/bags.html https://luma.your-store.test/gear/bags.html
CMS https://your-store.test/about-us https://luma.your-store.test/about-us
404 https://your-store.test/no-route https://luma.your-store.test/no-route
Search https://your-store.test/catalogsearch/result/?q=bag https://luma.your-store.test/catalogsearch/result/?q=bag

2. Pretty-print from the terminal

curl -ks https://your-store.test/chaz-kangeroo-hoodie.html \
 | perl -ne 'BEGIN{$/=undef} while (/<script type="application\/ld\+json"[^>]*>(.*?)<\/script>/gs) { print "$1\n" }' \
 | python3 -m json.tool

3. One-shot type summary across every page

for url in / /compete-track-tote.html /chaz-kangeroo-hoodie.html \
 /sprite-yoga-companion-kit.html /set-of-sprite-yoga-straps.html \
 /gear/bags.html /about-us /no-route; do
 types=$(curl -ks "https://your-store.test$url" \
 | perl -ne 'BEGIN{$/=undef} while (/<script type="application\/ld\+json"[^>]*>(.*?)<\/script>/gs) { print "$1\n" }' \
 | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); print(",".join(n.get("@type","?") for n in d.get("@graph",[d])))')
 printf " %-40s %s\n" "$url" "$types"
done

Expected output on a stock sample-data install:

 / Organization,WebSite
 /compete-track-tote.html Organization,WebSite,BreadcrumbList,Product,Review,MerchantReturnPolicy
 /chaz-kangeroo-hoodie.html Organization,WebSite,BreadcrumbList,Product,MerchantReturnPolicy
 /sprite-yoga-companion-kit.html Organization,WebSite,BreadcrumbList,Product,MerchantReturnPolicy
 /set-of-sprite-yoga-straps.html Organization,WebSite,BreadcrumbList,Product,MerchantReturnPolicy
 /gear/bags.html Organization,WebSite,BreadcrumbList,ItemList
 /about-us Organization,WebSite
 /no-route Organization

4. Browser DevTools one-liner

Paste in Chrome / Firefox / Safari DevTools console on any page:

JSON.parse(document.querySelector('script[data-panth-seo="jsonld"]').textContent)
 ['@graph'].forEach(n => console.log(n['@type'], '→', n.name || n.headline || n['@id']))

How to Validate with Google Search Console (Step by Step)

Google's Rich Results Test is the authoritative validator — it's what determines whether your pages get rich cards in SERPs. Here's the full flow.

Prerequisite: your site must be publicly reachable

Google's crawler can't reach localhost, *.test, VPN-internal, or intranet URLs. If you're testing locally, expose the site through a tunnel first:

# Option A: Cloudflare Tunnel (free, no signup for temporary tunnels)
cloudflared tunnel --url https://your-store.test

# Option B: ngrok (free tier fine for testing)
ngrok http https://your-store.test

Either gives you a public https://xxxx.trycloudflare.com/ or https://xxxx.ngrok.io/ URL. Use that in every validator below.

Step 1 — Google Rich Results Test

  1. Open search.google.com/test/rich-results.
  2. Paste your full product URL (e.g. https://xxxx.trycloudflare.com/compete-track-tote.html).
  3. Click Test URL. Google fetches the page.
  4. Once it finishes, you'll see one or more "Detected items" — Products, Breadcrumbs, Reviews, Merchant listings, etc.
  5. Click each item to expand. The left pane shows a live SERP preview card with your product image, price, rating stars, and availability.
  6. Any errors or warnings show at the top of each item panel. Errors block rich results; warnings are suggestions.

What to expect on a working product page:

  • Products — 1 item detected (valid)
  • Breadcrumbs — 1 item detected (valid)
  • Merchant listings — 1 item detected (valid) [if MerchantReturnPolicy enabled]
  • Reviews — N items detected [if product has reviews]

Step 2 — Submit your domain to Google Search Console

To track rich-result performance over time, add the whole site:

  1. Open search.google.com/search-console.
  2. Click Add property.
  3. Choose Domain (covers all subdomains + http/https) or URL prefix (for a specific scheme + host).
  4. Follow the verification flow — DNS TXT record for a domain property, or HTML file upload / meta tag / Google Analytics / Google Tag Manager for URL prefix.
  5. Once verified you'll see the Overview dashboard within 24 hours.

Step 3 — Submit your sitemap

If you have Panth_HtmlSitemap or any other sitemap generator, submit it:

  1. In Search Console → Sitemaps (left sidebar).
  2. Paste the sitemap URL (e.g. https://your-store.com/sitemap.xml).
  3. Click Submit. Google will crawl every URL in the sitemap and start detecting structured data.

Step 4 — Monitor rich-result eligibility

After Google re-crawls (typically 1–7 days):

  1. In Search Console → Enhancements (left sidebar).
  2. You'll see separate reports for every rich-result type your site emits:
  • Products (from Product schema)
  • Breadcrumbs (from BreadcrumbList schema)
  • Review snippets (from Review + AggregateRating)
  • Merchant listings (from Product + Offer with price + availability + merchant return policy)
  • Sitelinks searchbox (from WebSite + SearchAction)
  • FAQ (from FAQPage schema)
  • Logo (from Organization + logo)
  1. Each report shows Valid / Valid with warnings / Invalid URLs. Invalid URLs are the ones to fix first.

Step 5 — Fix any invalid items

For each invalid URL:

  1. Click the error type (e.g. "Missing field 'priceValidUntil'").
  2. Review the list of affected URLs.
  3. Open the URL in the Rich Results Test (Step 1) to see the exact problem.
  4. Fix the admin config — e.g. populate Default Price Valid Until or the missing brand attribute.
  5. Flush caches (bin/magento cache:flush config full_page).
  6. In Search Console, click Validate fix on the error. Google will re-crawl within days.

Step 6 — Request indexing (optional, speeds things up)

For a single URL:

  1. In Search Console → URL Inspection (top search bar).
  2. Paste a product URL.
  3. Click Request Indexing.
  4. Google will prioritize crawling that URL. Repeat for 10-20 of your most important pages.

Step 7 — Confirm rich results in SERP

After Google re-indexes, search for your product in site:your-store.com mode:

site:your-store.com compete track tote

Rich snippets (price, stars, availability badges, breadcrumb path above the title) should appear. Full rollout across all indexed pages takes 2-8 weeks.


Other Validators

Complement Google's Rich Results Test with these before shipping:

Tool What it checks URL
Schema.org Validator Stricter than Google — every schema.org issue, nested or not validator.schema.org
Facebook Sharing Debugger Open Graph + schema.org for Facebook preview cards developers.facebook.com/tools/debug
LinkedIn Post Inspector Schema preview for LinkedIn shares linkedin.com/post-inspector
Bing Webmaster Tools — Markup Validator Bing's equivalent of Rich Results Test bing.com/webmasters/tools/markup-validator
opengraph.xyz Multi-platform preview (Facebook, Twitter/X, LinkedIn, WhatsApp, Slack, iMessage, Discord) opengraph.xyz
Merkle Schema Markup Validator Batch-check multiple URLs technicalseo.com/tools/schema-markup-generator
Yandex Structured Data Validator Yandex-specific schema rules webmaster.yandex.com/tools/microtest

Per-Provider Deep Dive

ProductProvider

  • Runs on: product pages (current_product in registry).
  • Emits: Product with @id = {product_url}#product, full image list (from Magento Image helper + media gallery), description (meta_description → short_description → description, ≤5000 chars), brand (via Brand), audience (via PeopleAudience.audienceType from gender attribute), scalar Offer for simple products or offer-level extras (itemCondition, seller, priceValidUntil, shippingDetails, hasMerchantReturnPolicy) for variant types, AggregateRating from Magento's review summary, ISO 8601 dateModified / datePublished.
  • Availability logic: Discontinued (status=disabled or visibility=not_visible) → PreOrder (news_from_date in future) → BackOrder (backorders > 0) → OutOfStock → LimitedAvailability (qty < threshold) → InStock.
  • Variant types (configurable / bundle / grouped) skip scalar price / priceCurrency / availability / url — the specialised provider contributes those via deep-merge.

ConfigurableOfferProvider, BundleOfferProvider, GroupedOfferProvider

  • Run on: configurable, bundle, grouped products respectively.
  • Emit: a Product #product contribution containing an AggregateOffer with lowPrice / highPrice / offerCount and for Configurable / Grouped a child offers array of per-variant Offer nodes.
  • Bundle dynamic pricing pulls min/max from the bundle option price range. Bundle fixed pricing emits a scalar Offer.
  • All three merge into the same #product @id so the result is one coherent Product node.

OrganizationProvider + SellerProvider

  • Run on: every page (Organization) / product pages (Seller).
  • Emit: Organization #organization with full identity details.
  • SellerProvider uses the same @id = #organization by default; when business_type = LocalBusiness / Store / OnlineStore the merged @type is promoted to the more specific class.
  • sameAs combines 7 dedicated social-profile fields with a free-form textarea.

BreadcrumbProvider

  • Runs on: any page with a clear entity (product / category / CMS).
  • For products: picks the primary category from the product's category set, honoring admin-configured priority weights when enable_breadcrumb_priority = Yes, else tiebreaker on depth (longest / shortest).
  • Builds full hierarchy: Home → Category → Subcategory → Product (4 levels on most PDPs).

ProductListProvider

  • Runs on: category pages only.
  • Emits: ItemList with one ListItem per visible product in the current category view, preserving pagination order.

ReviewProvider

  • Runs on: product pages with reviews enabled (catalog/review/active).
  • Emits: up to 5 most-recent approved Review nodes with ISO 8601 datePublished, scaled 1-5 rating from Magento's 0-100 percent vote aggregate.
  • Defers to Panth_Testimonials on its own routes.

WebsiteProvider

  • Runs on: every page.
  • Emits: WebSite #website with SearchAction pointing at {store_url}catalogsearch/result/?q={search_term_string} — enables Google Sitelinks Search Box.

FaqExtractor

  • Runs on: any page with a description containing heading/paragraph Q&A pairs.
  • Parser: <h2>…?</h2><p>…</p> and <h3>…?</h3><p>…</p>.
  • Needs ≥2 Q&A pairs; defers to Panth_Faq on its routes.

CmsArticleProvider

  • Runs on: CMS pages whose identifier starts with blog/, news/, articles/ or whose meta keywords contain article.
  • Emits: Article with headline / description / mainEntityOfPage / url / author / publisher refs + ISO 8601 datePublished / dateModified + up to 5000 chars of articleBody.

ReturnPolicyProvider

  • Runs on: product pages when return_policy_days > 0.
  • Emits: MerchantReturnPolicy with applicableCountry from store config, merchantReturnDays (int), returnMethod, returnFees (mapped to ReturnFeesEnumeration URL).

VideoProvider

  • Runs on: product pages with media gallery entries of type external-video.
  • Emits: VideoObject for each video (YouTube / Vimeo).

Specialist providers

  • CertificationProviderhasCertification from a textarea attribute.
  • EnergyLabelProviderhasEnergyConsumptionDetails with grade A–G.
  • ProsConsProviderpositiveNotes / negativeNotes ItemLists.
  • SaleEventProviderpriceSpecification (UnitPriceSpecification) with validFrom/validThrough when special price is active.
  • DeliveryMethodProvider / PaymentMethodProvider / MultiRegionShippingProvider — Offer extensions for shipping + payment.
  • ProductGroupProviderProductGroup + hasVariant + variesBy (opt-in; richer variant modelling).
  • CustomPropertiesProvider — admin-editable JSON object deep-merged into the Product node for anything outside the built-in schemas.
  • BrandProvider — standalone Brand node alongside the inline Product.brand.

Troubleshooting

Issue Cause Resolution
No JSON-LD on any page FPC cache from before install bin/magento cache:flush config full_page
JSON-LD missing on specific page type Provider's isApplicable() returned false Check the provider's rules — e.g. ItemList needs a category with products; FAQPage needs ≥2 Q&A pairs
Duplicate Product schema in SERP remove_native_markup = No Flip to Yes + flush cache
Breadcrumbs only has 2 items (Home → Product) on PDPs with categories Old versions (pre-1.0.1) had a missing Config constant Upgrade to ≥ 1.0.1
Multi-select gender or brand attr silently drops entire Product schema Pre-1.0.2 (string) cast on array return Upgrade to ≥ 1.0.2
Configurable / bundle / grouped offers has leftover price key Pre-1.0.2 ProductProvider didn't gate scalar pricing for variant types Upgrade to ≥ 1.0.2
Google Rich Results Test: "Either 'offers', 'review', or 'aggregateRating' should be specified" Product has $0.00 final price or is out-of-stock with stock-based logic Set a price; check availability logic
Google: "Missing field 'priceValidUntil'" No special_to_date and admin default empty Set Default Price Valid Until to a future YYYY-MM-DD (module auto-falls-back to +1 year when both are empty)
Google: "Invalid URL in field 'image'" Product has no image and store logo is a CDN URL that 404s Upload a valid product image or store logo
Google: "Missing field 'returnFees'" Pre-1.0.2 emitted free-text returnFees that didn't match ReturnFeesEnumeration Upgrade to ≥ 1.0.2
FAQPage not detected Descriptions use a non-standard Q&A HTML shape Use <h2>Question?</h2><p>Answer.</p> — needs ≥2 pairs
SearchAction points at wrong URL Store base URL misconfigured Fix Stores → Configuration → Web → Base URLs
Organization name renders as "Default Store View" Store Information → Store Name not set Set it under Stores → Configuration → General → Store Information

Compatibility

Requirement Versions Supported
Magento Open Source 2.4.4, 2.4.5, 2.4.6, 2.4.7, 2.4.8
Adobe Commerce 2.4.4, 2.4.5, 2.4.6, 2.4.7, 2.4.8
Adobe Commerce Cloud 2.4.4 — 2.4.8
PHP 8.1.x, 8.2.x, 8.3.x, 8.4.x
MySQL 8.0+
MariaDB 10.4+
Hyva Theme 1.0+ (native support)
Luma Theme Native support
Required Dependency mage2kishan/module-core ^1.0

Works standalone (no other Panth module needed) and composes cleanly with Panth_AdvancedSEO, Panth_Faq, Panth_Testimonials when any of those are installed (specialist providers defer to those modules on their own routes).


Installation

Composer Installation (Recommended)

composer require mage2kishan/module-structured-data
bin/magento module:enable Panth_Core Panth_StructuredData
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush

Manual Installation via ZIP

  1. Download the latest release ZIP from Packagist or the Adobe Commerce Marketplace.
  2. Extract the contents to app/code/Panth/StructuredData/ in your Magento installation.
  3. Ensure Panth_Core is installed (required dependency).
  4. Run the same commands as above starting from bin/magento module:enable.

Verify Installation

bin/magento module:status Panth_StructuredData
# Expected output: Module is enabled

# Every page now carries one JSON-LD block:
curl -ks https://your-store.test/ | grep -c 'application/ld+json'
# Expected output: 1

Configuration

Navigate to Admin → Stores → Configuration → Panth Extensions → Structured Data. Four fieldsets:

Social Profiles (Organization sameAs)

Field Purpose
Facebook URL Full https://www.facebook.com/yourpage URL added to sameAs
Twitter (X) URL Full https://twitter.com/yourhandle URL
Instagram URL Full Instagram profile URL
LinkedIn URL Full LinkedIn company-page URL
YouTube URL Full YouTube channel URL
Pinterest URL Full Pinterest profile URL
TikTok URL Full TikTok profile URL

All URLs validated against validate-url and the module's internal HTTPS-only allowlist before being emitted.

Organization Details

Field Purpose
Legal Name legalName on the Organization node
Logo URL logo (ImageObject)
Phone contactPoint.telephone — overrides general/store_information/phone
Email contactPoint.email
Street / City / Region / Postal Code Full PostalAddress
Country (ISO 3166-1 alpha-2) addressCountry — e.g. US, GB, DE
Additional sameAs URLs One URL per line — appended to the social-profile sameAs array

Leave empty to fall back to core Magento Store Information.

Structured Data (JSON-LD)

Master toggles + attribute codes + defaults. Every toggle is Yes/No, every attribute code is a plain Magento attribute identifier.

Setting Default What it controls
Enable Product Yes Emits Product + Offer on product pages
Enable Breadcrumb Yes Emits BreadcrumbList everywhere
Enable Organization Yes Emits Organization on every page
Enable WebSite / SiteLinks Yes Emits WebSite + SearchAction
Enable Auto-Extracted FAQ Schema Yes Scans descriptions for Q&A pairs
Enable Article Yes Emits Article on blog-like CMS pages
Enable Review Yes Emits Review + AggregateRating
Enable VideoObject Yes Emits VideoObject for external-video gallery entries
Enable Brand Yes Emits Brand node
Enable Seller Yes Emits Seller (merged into #organization by default)
Configurable: Multi-Offer Yes Emits one child Offer per configurable variant
Remove Native Magento JSON-LD Markup Yes Strips Magento's native ld+json on product / breadcrumb blocks
Return Policy Days 30 merchantReturnDays — set to 0 to disable the MerchantReturnPolicy node
Brand Attribute Code manufacturer Product attribute used for Brand name
GTIN Attribute Code (empty) Product attribute for gtin / gtin13 / ean
MPN Attribute Code (empty) Product attribute for Manufacturer Part Number
Enable Product List Schema Yes ItemList on category pages
Accepted Payment Methods (empty) One method per line (Visa, Mastercard, PayPal)
Delivery Methods (empty) One method per line (Standard Shipping, Express)
Product Condition New itemCondition enum (New / Used / Refurbished / Damaged)
Default Price Valid Until (empty) YYYY-MM-DD — falls back to +1 year
Custom JSON-LD Properties (Product) (empty) JSON object merged into the Product node
Enable ProductGroup + hasVariant No Emits ProductGroup with variesBy on color / size / material
Enable Pros/Cons No Emits positiveNotes / negativeNotes ItemLists
Pros Attribute Code product_pros Textarea attribute for pros
Cons Attribute Code product_cons Textarea attribute for cons
Enable Energy Efficiency Label (EU) No Emits hasEnergyConsumptionDetails
Energy Class Attribute Code energy_class Attribute for grades A–G
Enable Product Certifications No Emits hasCertification
Certification Attribute Code certifications Textarea, one Authority | Name | ID per line
Enable Sale Event / Special Price Details Yes Emits priceSpecification with validFrom / validThrough
Seller Business Type Organization Organization / LocalBusiness / Store / OnlineStore
Default Brand Name (empty) Fallback when product has no brand attribute
Return Policy Type Refund refund / exchange (as additionalProperty)
Return Fees free free / ReturnFeesCustomerResponsibility / ReturnShippingFees / RestockingFees
Limited Stock Threshold 5 Below this → LimitedAvailability instead of InStock

Breadcrumbs

Setting Default What it controls
Enable Breadcrumb Priority No When Yes, product breadcrumbs use category priority weights
Breadcrumb Format Longest (deepest) shortest / longest — tiebreaker when priorities are equal

Every field is [store view]-scoped.

After any change, flush caches:

bin/magento cache:flush config full_page

License

Panth Structured Data is licensed under a proprietary license — see LICENSE.txt. One license per Magento installation.


More Information
Module Category SEO & Indexing
Best For All Sizes
FAQ

Frequently Asked Questions

Will this slow down my storefront?

No. The head block is cacheable="true" so the full JSON-LD payload is baked into full-page cache. Providers only run on uncached renders; cached hits serve the pre-rendered <script> tag with zero PHP evaluation.

Is it GDPR compliant?

The module only emits data already public on your storefront — product details, store identity, and aggregate review stats. It does not render personally identifiable data in structured-data form.

Can I disable individual schemas?

Yes. Every provider has a Yes/No toggle under Stores → Configuration → Panth Extensions → Structured Data — Product, Breadcrumb, Organization, WebSite, FAQ, Article, Review, VideoObject, Brand, Seller, Product List (ItemList), ProductGroup, Pros/Cons, Energy Label, Certifications, Sale Event, Remove Native Markup.

Does it work with Hyva?

Yes. No JS, no RequireJS, no x-magento-init. The head block attaches via view/frontend/layout/default.xml to the head.additional container which every Magento theme exposes. Identical output on Hyva, Luma, Breeze, and any custom theme.

Does it support multi-store / multi-language?

Yes. Every admin field is [store view]-scoped, so different store views can ship different Organization data, social profiles, toggles, and attribute codes. URLs in the JSON-LD are scoped to the current store's base URL.

Can I customize the emitted JSON?

Three ways, in increasing order of effort:

  1. Custom JSON-LD Properties (Product) — admin field that accepts a JSON object; merged into the Product node on every PDP.
  2. Per-product EAV attributes — the resolver reads product attributes like meta_description, short_description, manufacturer, gender — populate those for per-product overrides.
  3. Custom provider class — implement StructuredDataProviderInterface, register in di.xml under the providers argument of Panth\StructuredData\Model\StructuredData\Composite (and Aggregator). The aggregator will pick it up automatically.
Can I override a provider's output?

Yes — declare a virtualType in your own module's di.xml with the same class name as the provider you want to replace, or use a preference on Panth\StructuredData\Api\StructuredDataProviderInterface with a type constraint.

Does it interact with `Panth_AdvancedSEO`, `Panth_Faq`, `Panth_Testimonials`?

Yes, friendly:

  • Panth_AdvancedSEO: shares the panth_seo/general/enabled master switch when installed; defaults are set locally so standalone installs still work.
  • Panth_Faq: FaqExtractor defers to Panth_Faq on its own routes and when the faq.schema block is mounted and producing data.
  • Panth_Testimonials: ReviewProvider defers on the testimonials route when the module is enabled.
Is `Panth_Core` required?

Yes. mage2kishan/module-core is a required dependency and is pulled in automatically by Composer.

Does it emit `itemprop` / microdata?

No — only JSON-LD. Microdata is legacy and mixing the two often produces duplicate-structured-data warnings in Search Console. The RemoveNativeMarkupPlugin actively strips native Magento microdata from the target blocks.

Do I need a WhatsApp Business account?

No — any valid WhatsApp number works. However, a WhatsApp Business account is strongly recommended for commercial use (auto-replies, labels, catalog).

Does Panth Footer replace the default Magento footer?

Yes. When enabled, Panth Footer takes over the footer.container block and renders its own configurable footer. You can disable it any time to restore the default footer.

Will this slow down my storefront?

No. The notification feed is cached, assets are lightweight, and the JS runs asynchronously after page load.

Is llms.txt an official standard?

An emerging community standard proposed at llmstxt.org with a growing list of adopters (Anthropic, Perplexity, Vercel, Stripe, Cloudflare docs all publish one). Not an IETF RFC yet, but stable enough that major AI platforms rely on it.

Does this module change how Magento calculates custom option prices?

No. All price-delta logic continues to flow through Magento's standard priceBox and price-option JS components. This module only replaces the visual rendering.

Does Panth Mega Menu work on both Hyva and Luma?

Yes. The module ships with two purpose-built templates — Alpine.js + Tailwind for Hyva and vanilla JS for Luma — and auto-switches based on your active storefront theme.

How many forms can I create?

Unlimited. Each form has a unique identifier and its own submissions scope.

How is this different from a traditional antivirus?

Panth Malware Scanner is built specifically for Magento 2 filesystems — it understands the directory layout, knows which folders are writable from the frontend, and ships signatures tuned for Magento-targeted threats (Magecart skimmers, PolyShell webshells, admin-layout injection). Traditional AV tools scan everything with generic signatures and produce noise.

Do changes require a Tailwind rebuild every time?

No. Color, font, and radius changes use CSS custom properties and take effect instantly. Only changes to Tailwind-class-driven tokens (custom spacing scales, new breakpoints) require a rebuild via the Rebuild Theme button.

Do I have to pay for Panth Core?

No. Panth Core is completely free and will remain free forever. It is the foundation library that other (paid) Panth extensions depend on.

Need this customised?

Talk to Kishan directly — written quote, scope and timeline within 24 hours. No sales call.

WhatsApp

Structured Data JSON-LD for Magento 2

$0.00
Step up

Customers usually upgrade to