CollectionPageSchema
Outputs a CollectionPage with an ItemList of products; use it on category, listing and archive pages of a shop.
CollectionPageSchema describes a page that lists products, such as a shop category. It
outputs a CollectionPage whose mainEntity is an
ItemList of Product entries, each with its URL and, if
known, image and price. Google has no dedicated rich result for collection pages; the list
tells search engines which product pages belong to the category. The products’ own detail
pages use ProductSchema.
Usage
A category page built from a content collection of products:
---
// src/pages/shop/[category].astro
import { getCollection } from 'astro:content';
import { CollectionPageSchema } from '@casoon/astro-structured-data/components';
export async function getStaticPaths() {
const categories = await getCollection('categories');
return categories.map((category) => ({ params: { category: category.id }, props: { category } }));
}
const { category } = Astro.props;
const products = await getCollection('products', (p) => p.data.category === category.id);
---
<CollectionPageSchema
name={category.data.title}
description={category.data.description}
products={products.map((p) => ({
name: p.data.name,
url: `/products/${p.id}/`,
imageUrl: p.data.image,
price: p.data.price,
priceCurrency: p.data.price === undefined ? undefined : 'EUR',
}))}
/>
price and priceCurrency must be set together, so the currency is only passed for products
with a price. A prop with the value undefined counts as not set.
Output
On /shop/lamps/, for three products of which the last has no image and no price:
{
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "Lamps",
"description": "Desk, floor and pendant lamps.",
"url": "https://example.com/shop/lamps/",
"mainEntity": {
"@type": "ItemList",
"numberOfItems": 3,
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": {
"@type": "Product",
"name": "Arc Floor Lamp",
"url": "/products/arc-floor-lamp/",
"image": "/images/arc.jpg",
"offers": {
"@type": "Offer",
"price": 189,
"priceCurrency": "EUR"
}
}
},
{
"@type": "ListItem",
"position": 2,
"item": {
"@type": "Product",
"name": "Pixel Desk Lamp",
"url": "/products/pixel-desk-lamp/",
"image": "/images/pixel.jpg",
"offers": {
"@type": "Offer",
"price": "49.90",
"priceCurrency": "EUR"
}
}
},
{
"@type": "ListItem",
"position": 3,
"item": {
"@type": "Product",
"name": "Globe Pendant",
"url": "/products/globe-pendant/"
}
}
]
}
}
urlis the current page, built fromsiteUrland the page path.numberOfItemsand thepositionof each entry follow the order ofproducts.- Product URLs and images are written as given; root-relative paths are not resolved against
siteUrl. - A product with
pricegets anOfferwithpriceandpriceCurrency; no availability is added.
Props
| Prop | Type | Required | Default / fallback | Description |
|---|---|---|---|---|
id |
string |
No | – | Output as the page’s @id, so other nodes can reference it |
name |
string |
Yes | – | Name of the collection, e.g. the category |
description |
string |
Yes | – | Description of the collection |
products |
Product[] |
Yes | – | Listed products, at least one |
isPartOf |
{ type?, id?, name?, headline?, url? } or an array |
No | – | Reference by id or url; type is free text, default 'CreativeWork' |
hasPart |
{ type?, id?, name?, headline?, url?, position? }[] |
No | – | Reference by id or url; type is free text, default 'CreativeWork' |
changefreq, priority |
sitemap hints | No | – | See Meta tags and sitemap hints |
Each entry in products:
| Property | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Product name |
url |
string (URL or /path) |
Yes | Product detail page |
imageUrl |
string (URL or /path) |
No | Product image, output as image |
price |
number | string |
No | Price; non-negative, a string must be numeric like '49.90' |
priceCurrency |
string (ISO 4217) |
No | Currency, e.g. 'EUR' |
“URL or /path” means an absolute http(s) URL or a root-relative path. No other properties
are allowed in a product entry.
Rules
Violations fail the build (see Validation):
productsneeds at least one entry.- In each entry,
priceandpriceCurrencyare set together or not at all. nameanddescriptionmust not be empty.
The component uses no integration defaults apart from siteUrl and has no recommended
fields.