Product & Variants (Product, ProductGroup, Review, Offer)
What is product rich result?
Adding Product
markup to your webpage makes it eligible for a product snippet display. Product
snippets are enhanced text results that showcase additional product details like ratings, reviews, price, and availability.
ProductGroup
Products like apparel, shoes, furniture, electronics, and luggage often come in various sizes, colors, materials, or patterns. To help Google recognize these variants as part of the same parent product, use the ProductGroup
class with properties like variesBy, hasVariant, and productGroupID alongside Product structured data.
ProductGroup also allows you to specify common properties for all variants, such as brand and reviews, reducing duplicated information.
Kindly Refer Google Doc to know more about product rich result.
What are necessary data for product rich result?
1. Product name (rjs-prod-[number]-name
)
<div class="product">
<h1 class="rjs-prod-1-name">Denim Jacket</h1>
<div></div>
</div>
2. Images (rjs-prod-[number]-img
)
<div class="product">
<img
class="rjs-prod-1-img"
src="https://example.com/image1.jpg"
alt="Product Image 1"
/>
<img
class="rjs-prod-1-img"
src="https://example.com/image2.jpg"
alt="Product Image 2"
/>
<div></div>
</div>
3. Description (rjs-prod-[number]-desc
)
<div class="product">
<p class="rjs-prod-1-desc">
Product Description Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</p>
</div>
4. SKU ID / MPN Code (rjs-prod-[number]-sku
/ rjs-prod-[number]-mpn
)
<div class="product">
<p>
SKU ID:
<span class="rjs-prod-1-sku">123456789</span>
</p>
<p>
MPN Code:
<span class="rjs-prod-1-mpn">ABC123</span>
</p>
<div></div>
</div>
5. Brand name (rjs-prod-[number]-brand
)
<div class="product">
<p>
Brand:
<span class="rjs-prod-1-brand">XYZ Brand</span>
</p>
<div></div>
</div>
6. Reviews (rjs-prod-[number]-reviews
)
<div class="product">
<section class="rjs-prod-1-reviews">
<div class="urate">
<!-- rating value -->
<p class="rv">4.5</p>
<!-- max rate -->
<p class="mr">5</p>
<p class="raterP">John</p>
</div>
<div class="urate">
<!-- rating value -->
<p class="rv">4.5</p>
<!-- max rate -->
<p class="mr">5</p>
<p class="raterO">Foogle</p>
</div>
</section>
<div></div>
</div>
7. Aggregated rating (rjs-prod-[number]-aggrate
)
<div class="product">
<div class="rjs-prod-1-aggrate">
<!-- aggregated rate-->
<p class="arv">100</p>
<!-- max possibly rate -->
<p class="mr">100</p>
<!-- total user rate count -->
<p class="rc">1000</p>
</div>
<div></div>
</div>
8. Offer (rjs-prod-[number]-offer)
Offer includes several key components such as shipping details, return policy, price, purchase link, availability, and item condition.
Shipping Details
- Shipping cost -
rjs-prod-[number]-delcost
- Shipping destination -
data-delover
(data attribute) - Processing time -
rjs-prod-[number]-ptime
- Delivery time -
rjs-prod-[number]-ttime
data-range
is mandatory for processing & delivery time (see line no 8 &
12)
<div class="product">
<p>
Shipping Cost:
<span class="rjs-prod-1-delcost" data-delover="india"> 80 </span>
</p>
<p class="rjs-prod-1-ptime" data-range="1-3">
Processing Time: 1-3 business days
</p>
<p class="rjs-prod-1-ttime" data-range="3-5">
Estimated Delivery Time: 3-5 business days
</p>
</div>
Merchant Return Details
- Return Within:
rjs-prod-[number]-returnin
. - Return Fee:
rjs-prod-[number]-returnfee
.
<div class="product">
<p>
Return Policy:
<span class="rjs-prod-1-returnin">30 days</span>
</p>
<p>
Return Fees:
<span class="rjs-prod-1-returnfee">Free</span>
</p>
</div>
Validity (Reserved Names: productPriceValidUntilNext)
This is configured in richiejs.config.js
:
const config = {
reservedNames: {
product: {
productPriceValidUntilNext: 30; // Valid for 30 days from generation date
}
}
}
exports.default = config;
Purchase Link (Reserved Names: varientParameterName)
The purchase link is automatically generated based on the hierarchical structure of the input file. For files containing multiple variants on a single page, the link includes specific variant parameters in the URL, such as https://www.ashop.com/productPage?var=black_20_male.
Parameter name is defined as reservedNames.product.varientParameterName
in richiejs.config.js
. Configure this according to your web server’s logic of parameters handling.
const config = {
reservedNames: {
product: {
varientParameterName: "var",
},
},
};
exports.default = config;
Price & Currency
- Price: Marked with
rjs-prod-[number]-cost
. - Currency: Specified using
data-currency
(Alpha-3 code).
<div class="product">
<p>
Price:
<span class="rjs-prod-1-cost" data-currency="inr">
990
</span>
</p>
</div>
Availability (rjs-prod-[number]-avail)
Availability is boolean (true
or false
):
<div class="product">
<p>
Availability:
<span class="rjs-prod-1-avail" data-avail="true">In Stock</span>
</p>
</div>
Condition (rjs-prod-[number]-cond)
Possible values:
new
used
<div class="product">
<p>
Item Condition:
<span class="rjs-prod-1-cond" data-cond="new">New</span>
</p>
</div>
9. Varies By classes (data-var="color-audage-gender"
)
data-var
should be added in product name element
(rjs-prod-[number]-name
) element.
There are six type of variant class,
Color
- colorGender
- audageAge
- audageMaterial
- materialPattern
- patternSize
- size
Two or more classes can be added by using hyphen between them. Example:
color-audage-gender
- Element’s innerText value order should match them and
follow after name of the product. (see line number 4)
<div class="product">
<h1 class="rjs-prod-1-name" data-var="color-audage-gender">
Denim Jacket(Product Name) | Black | Age 20 | Male
</h1>
</div>
Function and parameters
By default input file is overwriiten with result so destination
is optional
This is only for API method. We prefer CLI method for keeping it simpler (refer - Working with API & CLI).
const richResultType = 'product';
const filePath = 'product.html';
const destination = 'dist/product.html'; /* optional */
richie([richResultType], filePath, destination);
Example of a Instance
<body>
<div class="product">
<!-- name -->
<h1 class="rjs-prod-1-name" data-var="color-audage-gender">
Denim Jacket | Black | Age 20 | Male
</h1>
<!-- images -->
<img
class="rjs-prod-1-img"
src="https://example.com/image1.jpg"
alt="Product Image 1"
/>
<img
class="rjs-prod-1-img"
src="https://example.com/image2.jpg"
alt="Product Image 2"
/>
<!-- description -->
<p class="rjs-prod-1-desc">
Product Description Lorem ipsum dolor sit amet, consectetur adipiscing
elit.
</p>
<!-- skuid -->
<p>
SKU ID:
<span class="rjs-prod-1-sku">123456789</span>
</p>
<!--mpn code -->
<p>
MPN Code:
<span class="rjs-prod-1-mpn">ABC123</span>
</p>
<!-- brand name -->
<p>
Brand:
<span class="rjs-prod-1-brand">XYZ Brand</span>
</p>
<p>
Price:
<span class="rjs-prod-1-cost" data-currency="inr">990</span>
</p>
<p>
Availability:
<span class="rjs-prod-1-avail" data-avail="true">In Stock</span>
</p>
<p>
Item Condition:
<span class="rjs-prod-1-cond" data-cond="new">New</span>
</p>
<p>
Return Policy:
<span class="rjs-prod-1-returnin">30 days</span>
</p>
<p>
Return Fees:
<span class="rjs-prod-1-returnfee">Free</span>
</p>
<!-- delivery across and return policy applicable country -->
<p>
Shipping Cost:
<span class="rjs-prod-1-delcost" data-delover="india">80</span>
</p>
<p class="rjs-prod-1-ptime" data-range=" 1-3">
Processing Time: 1-3 business days
</p>
<p class="rjs-prod-1-ttime" data-range="3-5">
Estimated Delivery Time: 3-5 business days
</p>
<!-- review -->
<section class="rjs-prod-1-reviews">
<div class="urate">
<!-- rating value -->
<p class="rv">4.5</p>
<!-- max rate -->
<p class="mr">5</p>
<p class="raterP">John</p>
</div>
<div class="urate">
<!-- rating value -->
<p class="rv">4.5</p>
<!-- max rate -->
<p class="mr">5</p>
<p class="raterO">Foogle</p>
</div>
</section>
<!-- aggregate review -->
<div class="rjs-prod-1-aggrate">
<!-- aggregate rate that got -->
<p class="arv">100</p>
<!-- max possibly rate -->
<p class="mr">100</p>
<!-- total user rate count -->
<p class="rc">1000</p>
</div>
</div>
</body>
Output of a Instance
<script type="application/ld+json">
[
{
"@context": "https://schema.org/",
"@type": "ProductGroup",
"name": "Denim Jacket",
"description": "Product Description Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"url": "https://www.cresteem.com/pages/productVarient/productCombined",
"brand": { "@type": "Brand", "name": "XYZ Brand" },
"productGroupID": "e965dfe6ea09a9fa90f45a5bad9c2730",
"variesBy": ["color", "suggestedAge", "suggestedGender"],
"hasVariant": [
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Denim Jacket",
"image": [
"https://example.com/image1.jpg",
"https://example.com/image2.jpg"
],
"description": "Product Description Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"sku": "123456789",
"mpn": "ABC123",
"offers": {
"@type": "Offer",
"category": "Fees",
"price": 990,
"priceCurrency": "INR",
"url": "https://www.cresteem.com/pages/productVarient/productCombined?var=black_20_male",
"priceValidUntil": "2024-05-05T18:13:20.000Z",
"availability": "InStock",
"itemCondition": "NewCondition",
"hasMerchantReturnPolicy": { "@id": "#return_policy" },
"shippingDetails": { "@id": "#shipping_policy" }
},
"audience": {
"@type": "PeopleAudience",
"suggestedGender": "MALE",
"suggestedAge": { "@type": "QuantitativeValue", "minValue": 20 }
},
"color": "Black"
},
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Denim Jacket",
"image": [
"https://example.com/image1.jpg",
"https://example.com/image2.jpg"
],
"description": "Product Description Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"sku": "123456789",
"mpn": "ABC123",
"offers": {
"@type": "Offer",
"category": "Fees",
"price": 990,
"priceCurrency": "INR",
"url": "https://www.cresteem.com/pages/productVarient/productCombined?var=brown_10_female",
"priceValidUntil": "2024-05-05T18:13:20.000Z",
"availability": "InStock",
"itemCondition": "NewCondition",
"hasMerchantReturnPolicy": { "@id": "#return_policy" },
"shippingDetails": { "@id": "#shipping_policy" }
},
"audience": {
"@type": "PeopleAudience",
"suggestedGender": "FEMALE",
"suggestedAge": { "@type": "QuantitativeValue", "minValue": 10 }
},
"color": "Brown"
}
],
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": 100,
"bestRating": 100,
"ratingCount": 2000
},
"review": [
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": 4.5,
"bestRating": 5
},
"author": { "@type": "Person", "name": "John" }
},
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": 4.5,
"bestRating": 5
},
"author": { "@type": "Organization", "name": "Foogle" }
},
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": 4.5,
"bestRating": 5
},
"author": { "@type": "Person", "name": "John" }
},
{
"@type": "Review",
"reviewRating": {
"@type": "Rating",
"ratingValue": 4.5,
"bestRating": 5
},
"author": { "@type": "Organization", "name": "Foogle" }
}
]
},
{
"@context": "https://schema.org/",
"@id": "#shipping_policy",
"@type": "OfferShippingDetails",
"shippingRate": {
"@type": "MonetaryAmount",
"value": 80,
"currency": "INR"
},
"shippingDestination": {
"@type": "DefinedRegion",
"addressCountry": "IN"
},
"deliveryTime": {
"@type": "ShippingDeliveryTime",
"handlingTime": {
"@type": "QuantitativeValue",
"minValue": 1,
"maxValue": 3,
"unitCode": "DAY"
},
"transitTime": {
"@type": "QuantitativeValue",
"minValue": 3,
"maxValue": 5,
"unitCode": "DAY"
}
}
},
{
"@context": "https://schema.org/",
"@id": "#return_policy",
"@type": "MerchantReturnPolicy",
"applicableCountry": "IN",
"returnPolicyCategory": "MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "ReturnByMail",
"returnFees": "FreeReturn"
}
]
</script>
Let’s see what’s next!