Panth Structured Data
mage2kishan/module-structured-data
Emits a single deduplicated JSON-LD @graph per Magento 2 page covering schema.org Product, Offer/AggregateOffer, BreadcrumbList, Organization, WebSite+SearchAction, ItemList, Review/AggregateRating, FAQPage, Article, VideoObject, and MerchantReturnPolicy, with full product-type coverage and automatic stripping of Magento native duplicate markup. Works on Hyva and Luma.
Build Tests
Code Quality
Tested on Magento 2.4.9
Recent Test History
Each release is tested against the latest Magento version at that time.
Top Contributors
View LeaderboardLooking for Contributors
Composer installation fails. Your contribution could help the entire Magento community!
Share This Module's Status
README
Loaded from GitHubPanth Structured Data β JSON-LD for Magento 2 (Hyva + Luma) | Panth Infotech
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.
π Need Custom Magento 2 Development?
Get a free quote for your project in 24 hours β custom modules, Hyva themes, performance optimization, M1βM2 migrations, and Adobe Commerce Cloud.
π Kishan Savaliya
Top Rated Plus on Upwork
100% Job Success β’ 10+ Years Magento Experience Adobe Certified β’ Hyva Specialist
π’ Panth Infotech Agency
Magento Development Team
Custom Modules β’ Theme Design β’ Migrations Performance β’ SEO β’ Adobe Commerce Cloud
Visit our website: kishansavaliya.com Β |Β Get a quote: kishansavaliya.com/get-quote
Table of Contents
- Admin Preview
- Key Features
- How It Works
- Schema.org Nodes Emitted
- Product Type Coverage
- Compatibility
- Installation
- Configuration
- Quick Test β URLs You Can Hit Right Now
- How to Validate with Google Search Console (Step by Step)
- Other Validators
- Per-Provider Deep Dive
- Troubleshooting
- FAQ
- Support
- About Panth Infotech
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.

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
@idand deep-merges so the Product node emitted byProductProviderand the AggregateOffer fromConfigurableOfferProviderbecome one coherent Product with one Offer, no collisions. - Native-markup strip plugin removes Magento's built-in
application/ld+jsonfromproduct.info.main,breadcrumbs,product.price.finalplus allitemprop/itemscope/itemtypemicrodata attributes on those blocks β so you never ship two Product schemas. - Own blocks are explicitly skipped β scripts carrying
data-panth-seoare preserved.
Full Product Type Coverage
- Simple β
Product+ scalarOffer(price / availability / itemCondition / seller / priceValidUntil / shippingDetails). - Configurable β
Product+AggregateOffer(lowPrice/highPrice/offerCount) with one childOfferper visible variant, each carrying its own price / sku / availability / url. - Bundle β
Product+AggregateOfferwithlowPrice/highPricepulled from the bundle option price range. Fixed-price bundles get a singleOfferwith the fixed final price. - Grouped β
Product+AggregateOfferwith one childOfferper 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+variesByoncolor/size/material.
Identity Nodes
- Organization β name / url / logo / legalName / contactPoint (phone + email) / PostalAddress /
sameAsarray 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
SearchActionfor Google Sitelinks Search Box eligibility. - Seller β auto-merged into
#organizationby default (sincebusiness_type = Organization); switches the@typetoLocalBusiness/Store/OnlineStorewhen 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
positionfor 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/acceptedAnswerpairs 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 carryingarticlein their meta keywords. - VideoObject β for product media gallery entries of type
external-video(YouTube / Vimeo).
Merchant / Pricing Extras
- MerchantReturnPolicy β
applicableCountry(ISO 3166-1),merchantReturnDays(int),returnMethod,returnFees(properly mapped to the schema.orgReturnFeesEnumerationURL). - Product pricing:
priceValidUntilfrom productspecial_to_dateβ admin default β auto +1 year fallback;availabilityfromisSalable()/StockRegistrywith LimitedAvailability / BackOrder / PreOrder / Discontinued beyond InStock / OutOfStock. - Sale Event (opt-in) β
priceSpecificationwithvalidFrom/validThroughon products with active special prices. - Delivery Methods β structured shipping details from admin-listed methods.
- Payment Methods β
acceptedPaymentMethodin Offer from admin list. - Multi-Region Shipping (auto-detect) β reads Magento's table-rate / flat-rate config and emits
shippingDetailsper region. Only when admin hasn't configured manualdelivery_methods.
Specialist Nodes
- Brand β standalone Brand node using the configured product attribute (default
manufacturer) + admin-configurable default fallback brand. - Certifications β
hasCertificationfrom a textarea attribute inAuthority | Name | IDformat. - Energy Efficiency Label (EU) β
hasEnergyConsumptionDetailswith grades AβG. - Pros / Cons β
positiveNotes/negativeNotesItemLists 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.additionalviaview/frontend/layout/default.xml. No JS, no RequireJS, nox-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, reviewdatePublished, article dates, salevalidFrom/validThroughall go throughDateTimeImmutable::format(ATOM)for strict schema.org compliance. - Attribute-text coerced β
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. - 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
- Applicability check β every provider exposes
isApplicable(). Product-scoped providers returnfalseon CMS pages,ReturnPolicyProviderreturnsfalsewhenreturn_policy_days = 0,FaqExtractordefers toPanth_Faqwhen that module owns the current page. Providers that returnfalseare skipped entirely. - Config gate β
Config::isStructuredDataEnabled($provider->getCode())readspanth_structured_data/structured_data/{code}. Admin disables any provider with a single Yes/No toggle. - Node collection β applicable + enabled providers'
getJsonLd()returns are collected. Single-node returns and list-of-nodes returns both work. - Dedup + deep-merge β two nodes with the same
@idget 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. - Document shape β one node in
@graphβ inline the node under@context. Multiple nodes β wrap in@graph. - 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.
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
- Download the latest release ZIP from Packagist or the Adobe Commerce Marketplace.
- Extract the contents to
app/code/Panth/StructuredData/in your Magento installation. - Ensure
Panth_Coreis installed (required dependency). - 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 |
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
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
- Open search.google.com/test/rich-results.
- Paste your full product URL (e.g.
https://xxxx.trycloudflare.com/compete-track-tote.html). - Click Test URL. Google fetches the page.
- Once it finishes, you'll see one or more "Detected items" β Products, Breadcrumbs, Reviews, Merchant listings, etc.
- Click each item to expand. The left pane shows a live SERP preview card with your product image, price, rating stars, and availability.
- 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:
- Open search.google.com/search-console.
- Click Add property.
- Choose Domain (covers all subdomains + http/https) or URL prefix (for a specific scheme + host).
- 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.
- 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:
- In Search Console β Sitemaps (left sidebar).
- Paste the sitemap URL (e.g.
https://your-store.com/sitemap.xml). - 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):
- In Search Console β Enhancements (left sidebar).
- You'll see separate reports for every rich-result type your site emits:
- Products (from
Productschema) - Breadcrumbs (from
BreadcrumbListschema) - Review snippets (from
Review+AggregateRating) - Merchant listings (from
Product+Offerwith price + availability + merchant return policy) - Sitelinks searchbox (from
WebSite+SearchAction) - FAQ (from
FAQPageschema) - Logo (from
Organization+logo)
- Products (from
- 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:
- Click the error type (e.g. "Missing field 'priceValidUntil'").
- Review the list of affected URLs.
- Open the URL in the Rich Results Test (Step 1) to see the exact problem.
- Fix the admin config β e.g. populate Default Price Valid Until or the missing brand attribute.
- Flush caches (
bin/magento cache:flush config full_page). - 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:
- In Search Console β URL Inspection (top search bar).
- Paste a product URL.
- Click Request Indexing.
- 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_productin registry). - Emits:
Productwith@id = {product_url}#product, full image list (from Magento Image helper + media gallery), description (meta_description β short_description β description, β€5000 chars), brand (viaBrand), audience (viaPeopleAudience.audienceTypefromgenderattribute), scalarOfferfor simple products or offer-level extras (itemCondition, seller, priceValidUntil, shippingDetails, hasMerchantReturnPolicy) for variant types,AggregateRatingfrom Magento's review summary, ISO 8601dateModified/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 scalarprice/priceCurrency/availability/urlβ the specialised provider contributes those via deep-merge.
ConfigurableOfferProvider, BundleOfferProvider, GroupedOfferProvider
- Run on: configurable, bundle, grouped products respectively.
- Emit: a
Product #productcontribution containing anAggregateOfferwithlowPrice/highPrice/offerCountand for Configurable / Grouped a childoffersarray of per-variantOffernodes. - 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 #organizationwith full identity details. SellerProvideruses the same@id = #organizationby default; whenbusiness_type = LocalBusiness / Store / OnlineStorethe merged@typeis promoted to the more specific class.sameAscombines 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:
ItemListwith oneListItemper 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
Reviewnodes with ISO 8601datePublished, scaled 1-5 rating from Magento's 0-100 percent vote aggregate. - Defers to
Panth_Testimonialson its own routes.
WebsiteProvider
- Runs on: every page.
- Emits:
WebSite #websitewithSearchActionpointing 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_Faqon its routes.
CmsArticleProvider
- Runs on: CMS pages whose identifier starts with
blog/,news/,articles/or whose meta keywords containarticle. - Emits:
Articlewith headline / description / mainEntityOfPage / url / author / publisher refs + ISO 8601datePublished/dateModified+ up to 5000 chars of articleBody.
ReturnPolicyProvider
- Runs on: product pages when
return_policy_days > 0. - Emits:
MerchantReturnPolicywithapplicableCountryfrom store config,merchantReturnDays(int),returnMethod,returnFees(mapped toReturnFeesEnumerationURL).
VideoProvider
- Runs on: product pages with media gallery entries of type
external-video. - Emits:
VideoObjectfor each video (YouTube / Vimeo).
Specialist providers
CertificationProviderβhasCertificationfrom a textarea attribute.EnergyLabelProviderβhasEnergyConsumptionDetailswith grade AβG.ProsConsProviderβpositiveNotes/negativeNotesItemLists.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 inlineProduct.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 |
FAQ
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:
- Custom JSON-LD Properties (Product) β admin field that accepts a JSON object; merged into the Product node on every PDP.
- Per-product EAV attributes β the resolver reads product attributes like
meta_description,short_description,manufacturer,genderβ populate those for per-product overrides. - Custom provider class β implement
StructuredDataProviderInterface, register indi.xmlunder theprovidersargument ofPanth\StructuredData\Model\StructuredData\Composite(andAggregator). 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 thepanth_seo/general/enabledmaster switch when installed; defaults are set locally so standalone installs still work.Panth_Faq:FaqExtractordefers toPanth_Faqon its own routes and when thefaq.schemablock is mounted and producing data.Panth_Testimonials:ReviewProviderdefers on thetestimonialsroute 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.
What's the smallest Magento version supported?
2.4.4. For earlier Magento versions, contact Panth Infotech β we can backport on request.
Support
| 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.
πΌ Need Custom Magento Development?
Looking for custom Magento module development, Hyva theme customization, store migrations, or performance optimization? Get a free quote in 24 hours:
Specializations:
- π Magento 2 Module Development β custom extensions following MEQP standards
- π¨ Hyva Theme Development β Alpine.js + Tailwind CSS, lightning-fast storefronts
- ποΈ Luma Theme Customization β pixel-perfect designs, responsive layouts
- β‘ Performance Optimization β Core Web Vitals, page speed, caching strategies
- π Magento SEO β structured data, hreflang, sitemaps, AI-generated meta
- ποΈ Checkout Optimization β one-page checkout, conversion rate optimization
- π M1 to M2 Migrations β data migration, custom feature porting
- βοΈ Adobe Commerce Cloud β deployment, CI/CD, performance tuning
- π Third-party Integrations β payment gateways, ERP, CRM, marketing tools
License
Panth Structured Data is licensed under a proprietary license β see LICENSE.txt. One license per Magento installation.
About Panth Infotech
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.
Quick Links
- π Website: kishansavaliya.com
- π¬ Get a Quote: kishansavaliya.com/get-quote
- π¨βπ» Upwork Profile (Top Rated Plus): upwork.com/freelancers/~016dd1767321100e21
- π’ Upwork Agency: upwork.com/agencies/1881421506131960778
- π¦ Packagist: packagist.org/packages/mage2kishan/module-structured-data
- π GitHub: github.com/mage2sk/module-structured-data
- π Adobe Marketplace: commercemarketplace.adobe.com
- π§ Email: kishansavaliyakb@gmail.com
- π± WhatsApp: +91 84012 70422
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
This content is fetched directly from the module's GitHub repository. We are not the authors of this content and take no responsibility for its accuracy, completeness, or any consequences arising from its use.