SakaniUI

Product Card

A product tile for grids and carousels, with pricing, rating, swatches, and stock state built in.

Ceramic pour-over mug

Ceramic Pour-Over Mug

$28.00

Handcrafted from natural stoneware clay, this minimal pour-over mug features a built-in ceramic dripper for slow, single-cup brewing. Dishwasher safe and made to last.

Linen table runner-30%

Linen Table Runner

$48.00$34.00

Woven from 100% European flax linen, this table runner adds effortless texture to any setting. Pre-washed for a soft, lived-in drape. Machine washable and naturally durable.

Oak serving boardOut of stock

Oak Serving Board

$56.00

A brief description of an oak serving board for an e-commerce store

Sale pricing is derived

There's no onSale flag: passing compareAtPrice is what produces the badge, the struck original, and the discounted price — and the saving is computed from the two numbers, so it can't contradict them.

Ceramic Pour-Over Mug-27%

Ceramic Pour-Over Mug

$38.00$28.00

Colour options

Ceramic Pour-Over Mug

Ceramic Pour-Over Mug

$38.00

Out of stock

Ceramic Pour-Over MugOut of stock

Ceramic Pour-Over Mug

$38.00

Props

PropTypeDefaultDescription
image / imageAltstringProduct photo and its alt text.
namestringProduct name.
descriptionstringShort supporting line.
pricenumberRaw number, formatted by the component.
compareAtPricenumberThe was-price. Its presence switches the card to the sale treatment.
rating / reviewCountnumberStar rating out of 5 and the number of reviews behind it.
colorsProductCardColor[]Swatches: { id?, color, label, available? }. id falls back to label.
onColorSelect(id: string) => voidFires with the selected swatch's id.
inStockbooleanFalse dims the card and blocks the add action.
wishlistedbooleanFills the wishlist heart.

Why it works this way

Why

  • A sale badge and struck-through price appear whenever a compare-at price is given, so the discount is derived from data rather than set by hand.

Don't

  • Don't set a compare-at price that is not a real previous price.