Product tiles for shops. ProductCard: the photo (a second photo on hover, or one photo per colour swatch), sale, new and sold-out badges, vendor and title (the title link covers the card), the price (sale price with the regular one struck through, or a range), star rating, colour swatches as native radios that swap the photo with CSS, and slots for quick add and wishlist. No cart logic inside. ProductGrid: a section with header and “Shop all”, 2–5 columns by container width, a list variant with the photo beside the text, an empty state and ItemList JSON-LD; bare renders only the list. productJsonLd builds the Product node for a product page.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { ProductCard, ProductGrid, productJsonLd, JsonLd } from "@vamos-ui/react/blocks";
<ProductGrid
title="New this *week*"
columns={4}
allLink={{ href: "/shop", label: "Shop all" }}
products={[{
title: "Morning cup", href: "/shop/morning-cup", image,
price: { amount: "28", currency: "USD" }, compareAt: { amount: "34", currency: "USD" },
rating: { value: 4.6, count: 128 },
swatches: [{ name: "Clay foot", color: "#e8dcc8", image: clayPhoto }, { name: "Chalk", color: "#f4f1ea", image: chalkPhoto }],
}]}
/>
// product page
<JsonLd data={productJsonLd(product, { url: "https://shop.example.com/shop/morning-cup", brand: "Kilnhouse" })} />Put productJsonLd on the product page only, not on every card in a grid.
Examples
Grid section (4 columns)
Header, “Shop all”, sale / new / sold-out badges, a price range and a hover photo. Columns step 4 → 3 → 2 → 1 with the container.
Fresh from the kiln
New this week
- −20%
- New
- Sold outLast batch
Kilnhouse
List variant (bare)
variant="list" puts the photo beside the text; bare drops Section and header. cardProps sets aspect, saleStyle and rangeStyle for every card.
- Sale
- New
Harbour Press
Wishlist and quick-add slots
Your own controls on the photo. The built-in + button (onQuickAdd) is the alternative to the quickAdd slot.
Kilnhouse
Phone width
One column; quick add is always visible on touch screens.
New this week
- −20%
- New
API
ProductCard
| Prop | Type | Default |
|---|---|---|
title / href / vendorThe title link covers the card; vendor sits above it. | string / string / ReactNode | — |
image / hoverImagePhoto, and a second photo shown on hover / focus. | SiteImage | — |
price / compareAtcompareAt above price → sale price, struck-through regular price, sale badge. | MoneyValue | — |
priceRange / rangeStyle“From $54” or “$54 – $88”. | { min; max } / "from" | "range" | — / "from" |
isNew / soldOut / badgeBadges top-start. soldOut also disables the built-in quick add. | boolean / boolean / ReactNode | — |
ratingStarRating, small. | { value; count? } | — |
swatches / maxSwatchesNative radios; up to 8 swap the photo with CSS; more show “+N more”. | { name; color; image?; soldOut? }[] / number | — / 5 |
swatchIndex / defaultSwatchIndex / onSwatchChangeControlled from a client component, or uncontrolled. | number / number / (i) => void | — |
quickAdd / onQuickAddYour own control, or a built-in round + button. | ReactNode / (swatchIndex) => void | — |
wishlistA control top-end on the photo. | ReactNode | — |
onNavigateRuns when the title link is followed. | (swatchIndex) => void | — |
layout / size / aspect / sizes / priority / headingLevelCard shape and photo loading. | "stack" | "row" / "md" | "lg" / ImageAspect / string / boolean / 2 | 3 | 4 | "stack" / "md" / "4:5" / — / — / 3 |
saleStyleSale badge “−20%” or “Sale”. | "percent" | "label" | "percent" |
labels / locale / componentsStrings, money format, Image / Link adapters. | Partial<CommerceLabels> / string / BlockAdapters | — |
ProductGrid
| Prop | Type | Default |
|---|---|---|
productsThe cards. | ProductCardData[] | — |
variant / columnsResponsive columns by container width, or one row per product. | "grid" | "list" / 2 | 3 | 4 | 5 | "grid" / 4 |
eyebrow / title / lead / allLinkSection header; the title also names the list. | ReactNode / SiteLink | — |
cardProps / renderCardShared card props, or render each card yourself. | Partial<ProductCardProps> / (product, i) => ReactNode | — |
priorityCountEager photos for the first row. | number | 0 |
bare / listLabelOnly the list (no section); its name when there is no title. | boolean / string | — |
emptyEmpty state. | { title?; text?; action? } | — |
jsonLd / siteUrlItemList JSON-LD of product URLs. | boolean / string | false |
theme / anchor / id / className / headingLevel / components / editModeShared block props. | BlockBaseProps | — |
productJsonLd(product, options)
| Prop | Type | Default |
|---|---|---|
optionsProduct with Offer / AggregateOffer, availability and AggregateRating. | { url?; sku?; brand?; description?; images? } | — |