One Block, One Graph, Zero Duplicates
Own blocks are explicitly skipped — scripts carrying data-panth-seo are preserved.
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…
Key Features:
Additional Services
Built-in from day one. No add-ons, no upsell, no licence keys to renew.
Own blocks are explicitly skipped — scripts carrying data-panth-seo are preserved.
Simple → Product + scalar Offer (price / availability / itemCondition / seller / priceValidUntil / shippingDetails).
Organization — name / url / logo / legalName / contactPoint (phone + email) / PostalAddress / sameAs array merging the 7 dedicated social…
BreadcrumbList — full category hierarchy for products (Home → Gear → Bags → Product), not just Home + Product. Respects admin-configured…
MerchantReturnPolicy — applicableCountry (ISO 3166-1), merchantReturnDays (int), returnMethod, returnFees (properly mapped to the schema.…
Brand — standalone Brand node using the configured product attribute (default manufacturer) + admin-configurable default fallback brand.
Structured Data — JSON-LD for Magento 2 (Hyva + Luma). Panth Structured Data emits schema.org JSON-LD for every page type in Magento 2 — Product / AggregateOffer / BreadcrumbList / Organization / WebSite / ItemList / Review / FAQPage / Article / VideoObject / MerchantReturnPolicy. Full product-type coverage (simple / configurable / bundle / grouped), strips Magento's native markup to avoid duplicates, auto-extracts FAQ pairs, supports Pros/Cons, Energy Label, Certifications, Sale Event, Multi-Region Shipping, and the full Organization sameAs stack. Theme-agnostic — identical output on Hyva and Luma. Compatible with Magento 2.4.4 - 2.4.8, PHP 8.1 - 8.4.
Engineered for magento 2 structured data, magento 2 json-ld, magento 2 schema.org — ships with sensible defaults, full admin controls, and zero JavaScript bloat. Works identically on Hyva and Luma; passes Adobe MEQP with zero severity-10 violations.
One JSON-LD block per page, product-type aware, theme-agnostic. Panth Structured Data emits a single
<script type="application/ld+json">with a deduplicated@graphcovering every schema.org node Google cares about — Product, Offer / AggregateOffer, BreadcrumbList, Organization, WebSite + SearchAction, ItemList, Review + AggregateRating, FAQPage, Article, VideoObject, MerchantReturnPolicy — with automatic strip of Magento's native duplicate markup. Identical output on Hyva and Luma, zero theme overrides.
Panth Structured Data is a production-grade schema.org JSON-LD module for Magento 2. A single head block feeds a pipeline of 24 content providers that each contribute one or more nodes to a shared @graph which an Aggregator deduplicates by @id and deep-merges so two providers contributing to the same entity always produce a single coherent node. A plugin on AbstractBlock::afterToHtml strips Magento's native application/ld+json scripts from product.info.main, breadcrumbs and product.price.final so you never ship two versions of the same schema. Every toggle, attribute code, and social URL lives under Admin → Stores → Configuration → Panth Extensions → Structured Data — nothing runs unless it's applicable on the current page.
Full product-type coverage: simple products emit Product + Offer, configurable emits Product + AggregateOffer with one child Offer per variant (15 variants → 15 offers), bundle emits Product + AggregateOffer with lowPrice / highPrice from the bundle price range, grouped emits Product + AggregateOffer with one child Offer per grouped SKU. Across all variant types the shared offer-level extras (itemCondition, seller @id → #organization, priceValidUntil, shippingDetails, hasMerchantReturnPolicy) ride through the deep-merge so rich-result validators always see a complete Offer.
Get a free quote for your project in 24 hours — custom modules, Hyva themes, performance optimization, M1→M2 migrations, and Adobe Commerce Cloud.
Kishan SavaliyaTop Rated Plus on Upwork 100% Job Success • 10+ Years Magento Experience Adobe Certified • Hyva Specialist |
Panth Infotech AgencyMagento Development Team Custom Modules • Theme Design • Migrations Performance • SEO • Adobe Commerce Cloud |
Visit our website: kishansavaliya.com | Get a quote: kishansavaliya.com/get-quote
<script type="application/ld+json" data-panth-seo="jsonld"> per page. Everything the module knows about the current entity lives inside one @graph.@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.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.data-panth-seo are preserved.Product + scalar Offer (price / availability / itemCondition / seller / priceValidUntil / shippingDetails).Product + AggregateOffer (lowPrice / highPrice / offerCount) with one child Offer per visible variant, each carrying its own price / sku / availability / url.Product + AggregateOffer with lowPrice / highPrice pulled from the bundle option price range. Fixed-price bundles get a single Offer with the fixed final price.Product + AggregateOffer with one child Offer per grouped SKU (name / price / availability / sku / url).Product + Offer.ProductGroup + variesBy on color / size / material.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.SearchAction for Google Sitelinks Search Box eligibility.#organization by default (since business_type = Organization); switches the @type to LocalBusiness / Store / OnlineStore when configured.position for rich carousel results.datePublished, scaled 1-5 rating from Magento's 0-100 percent votes.Question / acceptedAnswer pairs from product / category / CMS descriptions. Recognises <h2>…?</h2><p>…</p> and <h3>…?</h3><p>…</p> patterns.blog/*, news/*, articles/* or carrying article in their meta keywords.external-video (YouTube / Vimeo).applicableCountry (ISO 3166-1), merchantReturnDays (int), returnMethod, returnFees (properly mapped to the schema.org ReturnFeesEnumeration URL).priceValidUntil from product special_to_date → admin default → auto +1 year fallback; availability from isSalable() / StockRegistry with LimitedAvailability / BackOrder / PreOrder / Discontinued beyond InStock / OutOfStock.priceSpecification with validFrom / validThrough on products with active special prices.acceptedPaymentMethod in Offer from admin list.shippingDetails per region. Only when admin hasn't configured manual delivery_methods.manufacturer) + admin-configurable default fallback brand.hasCertification from a textarea attribute in Authority | Name | ID format.hasEnergyConsumptionDetails with grades A–G.positiveNotes / negativeNotes ItemLists from textarea attributes.material, audience, custom-vocabulary facets).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.cacheable="true", so FPC bakes the JSON-LD alongside the rest of the <head> and the providers only run on uncached renders.dateModified / datePublished, review datePublished, article dates, sale validFrom/validThrough all go through DateTimeImmutable::format(ATOM) for strict schema.org compliance.getAttributeText() 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.</ inside the JSON is escaped to <\/ so browsers can't be tricked into closing the <script> tag early.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) │
└─────────────────────────────────────────────────────────────────────┘
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.Config::isStructuredDataEnabled($provider->getCode()) reads panth_structured_data/structured_data/{code}. Admin disables any provider with a single Yes/No toggle.getJsonLd() returns are collected. Single-node returns and list-of-nodes returns both work.@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.@graph → inline the node under @context. Multiple nodes → wrap in @graph.</ in the serialised JSON is replaced with <\/ so the <script> tag can never be closed early by a malicious product name.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.
| 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.
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.
| 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).
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
app/code/Panth/StructuredData/ in your Magento installation.Panth_Core is installed (required dependency).bin/magento module:enable.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
Navigate to Admin → Stores → Configuration → Panth Extensions → Structured Data. Four fieldsets:
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.
| Field | Purpose |
|---|---|
| Legal Name | legalName on the Organization node |
| Logo URL | logo (ImageObject) |
| Phone | contactPoint.telephone — overrides general/store_information/phone |
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.
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 |
| 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
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.
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 |
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
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
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']))
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.
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.
https://xxxx.trycloudflare.com/compete-track-tote.html).What to expect on a working product page:
To track rich-result performance over time, add the whole site:
If you have Panth_HtmlSitemap or any other sitemap generator, submit it:
https://your-store.com/sitemap.xml).After Google re-crawls (typically 1–7 days):
Product schema)BreadcrumbList schema)Review + AggregateRating)Product + Offer with price + availability + merchant return policy)WebSite + SearchAction)FAQPage schema)Organization + logo)For each invalid URL:
bin/magento cache:flush config full_page).For a single URL:
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.
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 |
ProductProvidercurrent_product in registry).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.configurable / bundle / grouped) skip scalar price / priceCurrency / availability / url — the specialised provider contributes those via deep-merge.ConfigurableOfferProvider, BundleOfferProvider, GroupedOfferProviderProduct #product contribution containing an AggregateOffer with lowPrice / highPrice / offerCount and for Configurable / Grouped a child offers array of per-variant Offer nodes.Offer.#product @id so the result is one coherent Product node.OrganizationProvider + SellerProviderOrganization #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.BreadcrumbProviderenable_breadcrumb_priority = Yes, else tiebreaker on depth (longest / shortest).ProductListProviderItemList with one ListItem per visible product in the current category view, preserving pagination order.ReviewProvidercatalog/review/active).Review nodes with ISO 8601 datePublished, scaled 1-5 rating from Magento's 0-100 percent vote aggregate.Panth_Testimonials on its own routes.WebsiteProviderWebSite #website with SearchAction pointing at {store_url}catalogsearch/result/?q={search_term_string} — enables Google Sitelinks Search Box.FaqExtractor<h2>…?</h2><p>…</p> and <h3>…?</h3><p>…</p>.Panth_Faq on its routes.CmsArticleProviderblog/, news/, articles/ or whose meta keywords contain article.Article with headline / description / mainEntityOfPage / url / author / publisher refs + ISO 8601 datePublished / dateModified + up to 5000 chars of articleBody.ReturnPolicyProviderreturn_policy_days > 0.MerchantReturnPolicy with applicableCountry from store config, merchantReturnDays (int), returnMethod, returnFees (mapped to ReturnFeesEnumeration URL).VideoProviderexternal-video.VideoObject for each video (YouTube / Vimeo).CertificationProvider — hasCertification from a textarea attribute.EnergyLabelProvider — hasEnergyConsumptionDetails with grade A–G.ProsConsProvider — positiveNotes / negativeNotes ItemLists.SaleEventProvider — priceSpecification (UnitPriceSpecification) with validFrom/validThrough when special price is active.DeliveryMethodProvider / PaymentMethodProvider / MultiRegionShippingProvider — Offer extensions for shipping + payment.ProductGroupProvider — ProductGroup + 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.| 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 |
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.
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.
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.
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.
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.
Three ways, in increasing order of effort:
meta_description, short_description, manufacturer, gender — populate those for per-product overrides.StructuredDataProviderInterface, register in di.xml under the providers argument of Panth\StructuredData\Model\StructuredData\Composite (and Aggregator). The aggregator will pick it up automatically.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.
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.Panth_Core required?Yes. mage2kishan/module-core is a required dependency and is pulled in automatically by Composer.
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.
2.4.4. For earlier Magento versions, contact Panth Infotech — we can backport on request.
| Channel | Contact |
|---|---|
| kishansavaliyakb@gmail.com | |
| Website | kishansavaliya.com |
| +91 84012 70422 | |
| GitHub Issues | github.com/mage2sk/module-structured-data/issues |
| Upwork (Top Rated Plus) | Hire Kishan Savaliya |
| Upwork Agency | Panth Infotech |
Response time: 1-2 business days.
Looking for custom Magento module development, Hyva theme customization, store migrations, or performance optimization? Get a free quote in 24 hours:
Specializations:
Panth Structured Data is licensed under a proprietary license — see LICENSE.txt. One license per Magento installation.
Built and maintained by Kishan Savaliya — kishansavaliya.com — a Top Rated Plus Magento developer on Upwork with 10+ years of eCommerce experience.
Panth Infotech is a Magento 2 development agency specializing in high-quality, security-focused extensions and themes for both Hyva and Luma storefronts. Our extension suite covers SEO, performance, checkout, product presentation, customer engagement, and store management — over 34 modules built to MEQP standards and tested across Magento 2.4.4 to 2.4.8.
Browse the full extension catalog on the Adobe Commerce Marketplace or Packagist.
Ready to ship Google-validated structured data?
SEO Keywords: magento 2 structured data, magento 2 json-ld, magento 2 schema.org, magento 2 rich results, magento 2 product schema, magento 2 aggregateoffer, magento 2 breadcrumb schema, magento 2 organization schema, magento 2 review schema, magento 2 faqpage, magento 2 article schema, magento 2 videoobject, magento 2 merchantreturnpolicy, magento 2 product list schema, magento 2 itemlist, magento 2 seo schema, magento 2 google rich results, magento 2 sitelinks searchbox, magento 2 productgroup hasvariant, magento 2 pros cons schema, magento 2 energy label schema, magento 2 certifications schema, magento 2 saleevent schema, magento 2 multi region shipping, magento 2 delivery method schema, magento 2 payment method schema, magento 2 configurable product schema, magento 2 bundle product schema, magento 2 grouped product schema, hyva structured data, hyva json-ld, luma structured data, luma schema, mage2kishan structured data, panth infotech structured data, kishan savaliya magento, magento 2.4.8 structured data, magento 2 PHP 8.4 structured data, hire magento developer upwork, top rated plus magento freelancer, custom magento development, adobe commerce structured data, magento rich snippets, magento seo extension, magento schema generator
| Module Category | SEO & Indexing |
|---|---|
| Best For | All Sizes |
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.
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.
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.
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.
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.
Three ways, in increasing order of effort:
meta_description, short_description, manufacturer, gender — populate those for per-product overrides.StructuredDataProviderInterface, register in di.xml under the providers argument of Panth\StructuredData\Model\StructuredData\Composite (and Aggregator). The aggregator will pick it up automatically.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.
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.Yes. mage2kishan/module-core is a required dependency and is pulled in automatically by Composer.
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.
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.
Yes. The module ships dedicated Hyva templates built with Alpine.js and Tailwind CSS. Theme detection is automatic via 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.
No. Merchant-authored labels (anything other than empty, the product name, Image, main product photo, or the raw filename) are preserved. Only Magento's default placeholders get upgraded to template output.
No — any valid WhatsApp number works. However, a WhatsApp Business account is strongly recommended for commercial use (auto-replies, labels, catalog).
Yes, on the product detail page. When the module is enabled, the default gallery.phtml is replaced with the Panth gallery (one template for Hyva, another for Luma — chosen automatically).
Unlimited. Each form has a unique identifier and its own submissions scope.
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.
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.
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.
Talk to Kishan directly — written quote, scope and timeline within 24 hours. No sales call.
Structured Data JSON-LD for Magento 2