Product Card
NewA storefront card. Swatches crossfade between product photos, sizes can change the price, a wishlist heart pops and Add to bag confirms with a check.
motionlucide-reactutilsbadgebuttoncardfield@soultel/rolling-number@soultel/use-reduced-motion-safe
Installation
$ pnpm dlx shadcn@latest add @soultel/product-cardNo namespace set up? npx shadcn@latest add https://asafefe.dev/r/product-card.json works too.
Manual installation
- 1. Install the dependencies.
terminal1 lines npm install motion lucide-react - 2. Copy the source from the Code tab into components/product-card.tsx.
Usage
import { ProductCard, type Product } from "@/components/product-card"
const sneaker: Product = {
id: "runner-01",
brand: "Northside",
name: "Daybreak Runner",
price: 128,
compareAt: 150,
rating: 4.7,
reviews: 212,
badge: "New",
colors: [
{ name: "Ember", value: "#e2412f", image: "/products/runner-ember.jpg" },
{ name: "Ink", value: "#2b2a28", image: "/products/runner-ink.jpg" },
{ name: "Sage", value: "#7f9c7a", image: "/products/runner-sage.jpg", imageAlt: "Daybreak Runner in sage, side view" },
],
sizes: [{ label: "7" }, { label: "8" }, { label: "9" }, { label: "10", available: false }, { label: "11" }],
}
export function RunnerCard() {
return <ProductCard product={sneaker} onAddToCart={(selection) => addToCart(selection)} />
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| product | Product | - | id, name, price, colors { name, value, image, imageAlt }[], and optional brand, compareAt, rating, reviews, sizes { label, priceDelta, available }[], badge, and a fallback image and imageAlt. Photos render with a plain <img>, so any URL works without image config. |
| color / defaultColor | string | - | Controlled or initial color name. Defaults to the first color. |
| onColorChange | (color: ProductColor) => void | - | Called when a swatch is picked. |
| size / defaultSize | string | - | Controlled or initial size label. Defaults to the first available size. |
| onSizeChange | (size: ProductSize) => void | - | Called when a size is picked. |
| wishlisted / defaultWishlisted | boolean | false | Controlled or initial wishlist state. |
| onWishlistChange | (wishlisted: boolean) => void | - | Called when the heart is toggled. |
| onAddToCart | (selection: ProductSelection) => void | - | Called with productId, color, size, price and quantity when Add to bag is pressed. |
| currency | string | "$" | Symbol shown before prices. |
| sizeLabel | string | "Size" | Heading above the size picker, e.g. "Edition". |
