Product Options
The controls on a product detail page: price, swatches, sizes, quantity, stock, and wishlist.
Sand · M · ×1
Unavailable, not missing
ColorSwatch and SizeSelector both take available rather than expecting you to filter the list. Showing a greyed-out XL tells someone the size exists and is sold out; omitting it leaves them wondering whether you stock it at all.
Price
$38.00
$38.00$28.00Save 27%
Stock status
The wording and colour come from the number, so three states can't drift out of sync with the inventory count driving them.
In stockOnly 3 leftOut of stock
Sizes and quantity
Sand · M · ×1
PriceDisplay props
| Prop | Type | Default | Description |
|---|---|---|---|
| price | number | Current price, as a raw number. | |
| compareAtPrice | number | The was-price; switches to the sale treatment. | |
| showBadge | boolean | false | Adds a "Save X%" badge computed from the two prices. No-op without compareAtPrice. |
| formatPrice | (amount: number) => string | Override the default USD formatting. |
The option controls
| Prop | Type | Default | Description |
|---|---|---|---|
| ColorSwatch | color, label, selected, available, onSelect | label is the accessible name — colour alone isn't one. available={false} renders it disabled rather than hiding it, so people can see the option exists. | |
| SizeSelector | size, selected, available, onSelect | Same pattern: unavailable sizes stay visible but unclickable. | |
| QuantitySelector | quantity, onQuantityChange, min, max, label | Controlled. label names the group for screen readers, so include the product. | |
| StockStatus | quantity, lowStockThreshold, label | Derives its own text and colour from the number; lowStockThreshold defaults to 5. label overrides the text but keeps the derived colour. | |
| WishlistButton | saved, onToggle, label | onToggle receives the new saved state. |