Purpose#
ProductStructuredData emits Schema.org JSON-LD (Product/ProductGroup) and an AI-agent context block for a single product page, so search engines can produce rich results and LLM/agentic shopping consumers get a concise, high-density metadata block.Where it's rendered#
Rendered from Components/ProductSingle/Default.liquid:{% render 'Reusables\ProductStructuredData\Default', product: product, canonicalUrl: canonicalUrl, showPrices: showPrices %}
The product object for the current product page.
The canonical URL used for url/mainEntityOfPage-style fields.
Boolean; when false, price/offer blocks are omitted (e.g. B2B storefronts that hide prices).
Model shape (storefront example)#
{
"id": "Product Id",
"title": "Sample product title",
"description": "<p>Sample description</p>",
"sku": "Sample text",
"mpn": "Sample text",
"isSkuVisible": true,
"isMpnVisible": true,
"tags": ["sample-tag"],
"brand": { "name": "Sample brand" },
"mediaItems": [{ "link": "https://example.com/sample.jpg" }],
"stockAvailability": { "label": "InStock" },
"attributes": [{ "name": "Sample text", "attributeItemValue": "Sample text" }],
"extraFields": [{ "label": "Sample text", "value": "Sample text" }],
"productVariants": [
{ "id": "Variant Id", "title": "Sample text", "sku": "Sample text", "finalPrice": 25, "stockAvailability": { "label": "InStock" } }
],
"startPrice": 25
}
Template behavior (Liquid + Alpine)#
Resolves currency from GlobalData.Company.Currency (defaults to EUR), and availability from product.stockAvailability.label / each variant's stockAvailability.label.
Emits @type: ProductGroup with a hasVariant array when product.productVariants.size > 1; otherwise emits @type: Product with a single offers block (falling back from the single variant's finalPrice to product.startPrice, or a bare productID when neither is available).
All string values pass through | serialize (JSON-encode + escape) so quotes in titles/descriptions can never break the JSON.
Also emits a second <script id="ai-agent-context" type="application/json"> block with a compact spec/variant/API-route summary aimed at LLM/agentic shopping consumers.
Renders nothing when product is null.
Data contract (JS runtime)#
No client-side runtime state; all data is read directly from the Liquid render parameters at render time.JavaScript#
File: Reusables/ProductStructuredData/Default.js
Exposes: productstructureddatareusabledefault (currently an empty object)The JSON-LD and agent-context blocks are fully server-rendered; the global only reserves a unique name in the concatenated componentscripts.js bundle.Global Alpine stores#
None. This reusable does not read or update any Alpine store.Services / API calls#
None. All data (product/pricing/availability) is passed in as render parameters; there are no client-side API calls.Dependencies#
Liquid: Reusables/ProductStructuredData/Default.liquid
JS: Reusables/ProductStructuredData/Default.js
Translations: Reusables/ProductStructuredData/Default.json
Notes#
Both <script> blocks are omitted entirely when product is null.
The ai-agent-context block intentionally lists real API routes (livePricing, productData, addToCart) so agentic consumers can act on the data; keep these routes in sync if the underlying endpoints change.
Modified at 2026-07-24 07:43:32