Sell More Using Structured Data in Your Vendure Webshop

Niels
Learn how to boost e-commerce sales with structured data in Vendure. Step-by-step recipe-style guide, including JSON-LD examples and best practices for product pages.

Sell More Using Structured Data in Your Vendure Webshop
Why Structured Data Matters
We all want to sell more through our online stores, and everyone says structured data is more important than ever, especially with LLMs. As if structured data wasn’t useful a couple of years ago! I’m not going to go too much into that or pretend this guide is a silver bullet, but communicating to search engines what you sell or talk about is very helpful. It always has been and always will.
Structured data helps Google understand whether you have something people are looking for, and it helps you ensure that search engines correctly understand your products and content.
We’re not going to start by defining structured data, you probably already know what it is or can check any SEO guide. Here, we focus specifically on e-commerce and using JSON-LD, nothing else.
Ingredients
- Access to a developer – Someone who can help you implement structured data if you cannot implement it yourself. Don't make developers grumpy; they appreciate clear instructions!
- Google’s Rich Result Tester or Schema Markup Validator – Keep this open to test your structured data.
- Understanding your tech stack – You’ll want to automate the JSON-LD for all pages. In our case, we use a headless setup with Vendure, AstroJS, and Directus CMS.
Preparation: Mise en Place
Before you start, audit your existing pages and products. Which pages are most important for SEO and conversion? Collect product data like:
- Titles
- Prices
- Descriptions
- Availability
- Reviews
- Images
This is your raw material for structured data. Think of it as washing and chopping your ingredients before cooking.
Vendure Users: Leverage Your Product Fields
If you’re running your shop on Vendure, the easiest and most reliable way to populate your JSON-LD is to reuse the existing product fields from Vendure. Most of the information you need, like name
, description
, price
, images
, and variants
, is already available in the GraphQL schema.
By pulling directly from these fields, you ensure that your structured data is always up-to-date and consistent with your product catalog. No need for duplicate data entry or manual updates!
A great resource to explore the available product fields is Vendure’s official documentation on the GraphQL Product object:
Vendure GraphQL API: Product Object
Instructions: Adding JSON-LD to Product Pages
- Generate a base template – Include the necessary fields: name, price, SKU, availability, and reviews.
- Map product fields to schema.org properties – Ensure everything corresponds to the correct JSON-LD property.
- Automate injection via your tech stack – Let Vendure + Astro handle adding the JSON-LD to each product page.
- Test each page – Use Google’s Rich Results Test or Schema Markup Validator to ensure your structured data works correctly.
Generate a base template
Before you start, create a template that defines which fields will appear in your JSON-LD. At minimum, include:
name
– The product’s titledescription
– Short, descriptive textprice
– Numerical value of the productsku
– Unique stock keeping unitavailability
– In stock, out of stock, etc.images
– At least one product image URLaggregateRating
– If available
Map Product Fields to schema.org Properties
Once your template is ready, you need to connect each product field to the corresponding schema.org property. This ensures Google interprets your data correctly.
Handling Variants in Vendure + JSON-LD
Are you selling products in different weights, sizes, or colors? In Vendure, those are variants (each with its own sku
, price, and stock), exposed via the GraphQL API as variants
(aka productVariants
).
In structured data, the clearest, Google-approved way to represent this on one URL is to model the parent as a ProductGroup
and each option as a Product
under hasVariant
, with a backlink from the child using isVariantOf
.
Add variesBy
to tell Google which dimensions change (e.g. "weight"
, "size"
, "color"
).
Why this approach?
So each entry in the feed is a variant. It groups everything cleanly, prevents duplicate or conflicting markup when all options live on the same page, and aligns with Google’s variant guidance introduced in 2024.
Vendure → JSON-LD Variant Mapping
Vendure (GraphQL) | JSON-LD Property | Notes |
---|---|---|
variant.product.name |
ProductGroup.name |
Parent product title. |
variant.product.slug → page URL |
ProductGroup.url / @id |
Use the canonical product URL as the group’s url and @id . |
variant.product.description |
ProductGroup.description |
General description shared by variants. |
variant.product.featuredAsset |
ProductGroup.image |
One or more absolute image URLs. |
variant.name |
Product.name (inside hasVariant ) |
Variant-specific display name. |
variant.sku |
sku |
Unique per variant (Vendure treats each variant as the actual purchasable SKU). |
variant.price* + variants[].currencyCode |
offers.price + offers.priceCurrency |
Price belongs in an Offer ; currency must be an ISO-4217 code (e.g. EUR ). Vendure models money and currencies explicitly. |
Stock signal (e.g. variant.stockOnHand > 0 ) |
offers.availability |
Use the Schema.org URLs like https://schema.org/InStock or https://schema.org/OutOfStock . |
Distinguishing attributes (e.g. weight, color) | variesBy (parent) + additionalProperty on each variant |
Use PropertyValue for arbitrary attributes; for common ones, you may use color , size , etc. |
Example
{
"@context": "https://schema.org",
"@type": "ProductGroup",
"name": "Example Product",
"description": "Available in multiple weights.",
"url": "https://example.com/product/example-product",
"image": [
"https://example.com/images/example-500g.jpg",
"https://example.com/images/example-1kg.jpg"
],
"variesBy": ["weight"],
"hasVariant": [
{
"@type": "Product",
"name": "Example Product 500g",
"sku": "EX500G",
"isVariantOf": {
"@type": "ProductGroup",
"@id": "https://example.com/product/example-product"
},
"offers": {
"@type": "Offer",
"price": "9.99",
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock"
},
"additionalProperty": [
{ "@type": "PropertyValue", "propertyID": "weight", "value": "500 g" }
]
},
{
"@type": "Product",
"name": "Example Product 1kg",
"sku": "EX1KG",
"isVariantOf": {
"@type": "ProductGroup",
"@id": "https://example.com/product/example-product"
},
"offers": {
"@type": "Offer",
"price": "17.99",
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock"
},
"additionalProperty": [
{ "@type": "PropertyValue", "propertyID": "weight", "value": "1 kg" }
]
}
]
}
Automate Injection via Vendure
Manually adding JSON-LD to every product page is tedious and error-prone. Automate it using your stack, in our case it looks like this:
- Vendure + Astro: Fetch product data via GraphQL and inject JSON-LD dynamically when generating pages, as mentioned above.
- Headless CMS (e.g., Directus): Store custom fields and merge them into your JSON-LD template.
- Server-Side Rendering: Render JSON-LD directly into the
<head>
during page generation.
Testing is Essential
Always test with:
- Google Rich Results Test
- Schema.org Validator
- Google Search Console
Think of it as tasting your dish before serving it to guests, don’t serve broken markup!
Serving Suggestions: Next Steps
After implementing structured data:
- Monitor performance in Google Search Console
- Experiment with other schemas like
FAQPage
orHowTo
for content pages
Chef’s Tips: Common Mistakes to Avoid
- Always check JSON-LD syntax; one missing comma can break your markup
Example: Eifelgold Lavameel Structured Data
Here’s an exact JSON-LD example for the product Eifelgold Lavameel from Wormenkwekerij Wasse:
```json
<script type="application/ld+json">
{
"@context": "https://schema.org/",
"@type": "Product",
"name": "Eifelgold lavameel 20 kg",
"image": "https://storage.googleapis.com/shops-wkw-assets-prod/presets/0b/lavameel-20kg-22140_preview.webp",
"description": "Bestel Eifelgold lavameel van topkwaliteit. 100% natuurlijk, rijk aan mineralen en perfect voor een gezonde moestuin. Snelle levering!",
"offers": {
"@type": "Offer",
"price": "19.90",
"priceCurrency": "EUR",
"priceValidUntil": "2026-10-02T11:30:17+00:00",
"availability": "http://schema.org/InStock"
},
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "4.9",
"bestRating": "5",
"ratingCount": "29"
}
}
</script>
This approach makes it easy to automate JSON-LD generation for all your products, keeping your structured data accurate and fully optimized for search engines. Please feel free to reach out if this made your selected developer, or yourself grumpy. We'll be able to implement it for you!
Enjoy these fun sources as well: