Storefront Next ships built-in support for Answer Engine Optimization (AEO) and Generative Engine Optimization (GEO) on the Product Detail Page (PDP) and Product Listing Page (PLP). AEO makes product and category data machine-readable so search engines, voice assistants, and AI-powered answer surfaces can accurately surface facts—name, price, availability, brand—without guessing from unstructured HTML. GEO supplies clear entity structure and aligned metadata so generative and retrieval-augmented AI systems can ground their responses in your storefront’s authoritative data. When a user asks an AI assistant about a product or category you sell, GEO increases the likelihood that the AI cites your storefront as the source.
AEO and GEO have these benefits:
Rich results in search engines: Structured Product data enables Google rich snippets: price, availability, and review stars inline in search results.
AI citation and grounding: Clear entity graphs (products, offers, breadcrumbs) give Large Language Models (LLMs) and Retrieval-Augmented Generation (RAG) systems verifiable facts to cite rather than fabricated summaries.
Voice and assistant surfaces: Answer engines can resolve who, what, how much, and in-stock queries directly from your structured data.
Multisite consistency: URL helpers respect site/locale path prefixes and proxy headers, so structured data links are correct across all your storefronts without manual configuration.
Zero page-breakage risk: Schema generation runs in a deferred Promise. If it fails, the page renders normally with no script tag or broken pages.
How It Works
Two technical mechanisms work together: JSON-LD and SeoMeta.
Mechanism
Purpose
Where
JSON-LD (<script type="application/ld+json">)
Structured entity data for crawlers and AI systems
PDP and PLP route loaders → JsonLd component
SeoMeta
Classic <title>, <meta name="description">, Open Graph tags
ProductContent (PDP) and category route (PLP)
JSON-LD is the primary AEO and GEO signal. SeoMeta complements it—crawlers and social previews still read <head> tags even when structured data is present.
Product Detail Page (PDP)
What Gets Emitted
The PDP emits a schema.org/Product graph with these fields populated from B2C Commerce API (SCAPI) data when available.
Field
Source
name
product.name
description
longDescription, falling back to shortDescription, then pageDescription if earlier fields are empty
image
Up to 5 large or medium images (thumbnails excluded)
sku, productID
product.id
url
Public storefront origin + current path
brand
product.brand
mpn
product.manufacturerSKU
gtin
product.ean
category
product.primaryCategoryId
color
First color variation attribute value
additionalProperty
All variation attributes + custom attributes as PropertyValue
offers.price
Effective price (base or promo)
offers.priceCurrency
product.currency
offers.availability
Derived from inventory (InStock, OutOfStock, BackOrder, and PreOrder)
offers.priceValidUntil
One year from generation date (default horizon)
offers.lowPrice and highPrice
Populated for master products with a price range across variants
SeoMeta sets <title> to the product name, meta description to pageDescription or shortDescription, and Open Graph og:type to product with the page URL and primary image.
The PLP emits a schema.org/CollectionPage with a mainEntity of type ItemList.
CollectionPage
name: Category name
description: category.pageDescription, when present
url: Canonical category URL
ItemList (inside mainEntity)
numberOfItems: Total from search result
itemListElement: Up to 24 products, each as a ListItem wrapping a lightweight Product (name, url, image, offers)
Pricing
Each list item uses the lowest promotional price when promotions exist. Otherwise, it uses the base price. For master products, the schema considers variant-level prices.
Availability
The orderable field on the search hit drives availability. If the hit doesn’t expose orderability but config.search.products.refine.orderableOnly is true, then the storefront infers InStock availability for all listed hits.
BreadcrumbList
Built from parentCategoryTree when present, plus the current category. All category URLs use buildCategorySchemaUrl to preserve site/locale prefixes.
The loader merges critical and non-critical search hits before schema generation, so the ItemList reflects the full first page of results, not just the above-the-fold slice.
Note
SeoMeta
Sets <title> to the category name, meta description to pageDescription or the general description, and og:type to website with the canonical page URL.
Storefront Next builds all JSON-LD URLs from the public storefront origin, not from product.slugUrl or internal API URLs. This approach is critical in serverless or proxied environments, such as Managed Runtime and content delivery networks (CDNs), where request.url can contain internal routing addresses.
File:
1src/utils/schema-url.ts
Key functions:
1// Reads x-forwarded-host / x-forwarded-proto headers to resolve the public domain2getPublicOrigin(request: Request): string34// Builds product and category URLs that preserve the site/locale path prefix5buildProductSchemaUrl({productId, origin, currentPageUrl}): string | undefined6buildCategorySchemaUrl({categoryId, origin, currentPageUrl}): string | undefined
Example: Multisite URL Preservation
1// Current page: https://example.com/global/en-GB/category/womens2// Built product URL:3buildProductSchemaUrl({4 productId: "12345",5 origin: "https://example.com",6 currentPageUrl: "https://example.com/global/en-GB/category/womens",7});8// → 'https://example.com/global/en-GB/product/12345'
Storefront Next extracts the site/locale prefix (for example, /global/en-GB) from the current page URL and prepends it to every linked URL inside the schema.
Rendering
Both PDP and PLP inject JSON-LD via the JsonLd component, which renders a <script type="application/ld+json"> tag. The loader defers schema generation and returns an unresolved Promise. JsonLd is wrapped in <Suspense> using React’s use() so that it streams with Server-Side Rendering (SSR) after product data is ready.
1// JsonLd component usage (already wired in the routes)2import { JsonLd } from "@/components/json-ld";34<Suspense fallback={null}>5 <JsonLdWrapper schemaPromise={loaderData.productSchema} id="product-schema" />6</Suspense>;
If generateProductSchema or generateCategorySchema throws, the error is logged in development and no script tag is rendered. The page continues to load normally.
Script IDs:
PDP: id="product-schema"
PLP: id="category-schema"
Customizing the Schema
Add Aggregate Ratings (PDP)
The ProductSchema type already includes an aggregateRating field. Wire it in product-schema.ts after fetching rating data from your review provider:
The quality of AEO/GEO signals depends directly on the data in your B2C Commerce catalog. Make sure that these Business Manager fields are populated for every product and category.
pageDescription: Used as the meta description and JSON-LD description
longDescription or shortDescription: Fallbacks for JSON-LD description
brand: Populates schema:brand
ean: Populates schema:gtin
manufacturerSKU: Populates schema:mpn
Google’s Rich Results Test flags structured data that contradicts visible on-page content as a quality issue. Keep catalog fields in sync with what shoppers see.