Colour and size for one product, on Base UI radio groups, with the price and stock line above and below.
When to use
- A product page's buy box: pick a colourway, then a size, before Add to bag.
- Any variant matrix where stock differs per combination and shoppers should see it before they choose.
- Over a Select: swatches and sizes are visible at once, and sold-out options read at a glance.
When not to use
- More than two option axes (colour, size and length): compose two pickers or use Select for the third.
- Choosing from a grid of products: use Product Card, whose quick add has its own size row.
Installation
npx shadcn add https://eased.dev/r/variant-picker.jsonThe command adds Variant Picker, everything it builds on and Eased’s tokens to your project.
Usage
import { VariantPicker } from "@/components/eased/variant-picker/variant-picker";export function BuyBox() { const [id, setId] = useState("p1-0-S"); return ( <VariantPicker value={id} onValueChange={setId} variants={[ { id: "p1-0-S", color: "Moss", swatch: "#55663f", size: "S", stock: 12, price: 248, compareAt: 310 }, { id: "p1-0-M", color: "Moss", swatch: "#55663f", size: "M", stock: 0, price: 248, compareAt: 310 }, { id: "p1-2-S", color: "Clay", swatch: "#a8623f", size: "S", stock: 2, price: 198, compareAt: 310 }, ]} /> );}API reference
VariantPicker
| Prop | Type | Default | Description |
|---|---|---|---|
variants | ProductVariant[] | { id, color, swatch, size, stock, price, compareAt? }. Colours and sizes are read in order. | |
value / defaultValue | string | first in stock | The selected variant id. |
onValueChange | (id, variant) => void | ||
showPrice | boolean | true | The price line with its markdown badge. |
lowStockAt | number | 3 | At or under this, the stock line reads "Only n left". |
sizeGuide | ReactNode | Trailing content on the size label row. | |
inStockLabel | string | "In stock, ships tomorrow" | |
currency / locale | string / Intl.LocalesArgument | "USD" / "en-US" |
Picking a colour keeps the size if that colour comes in it, otherwise it moves to the colour's first size in stock. Sold-out sizes stay selectable so the shopper can ask to be notified.
Keyboard
| Key | Action |
|---|---|
Tab | Moves between the colour group and the size group (one stop each). |
| Arrows | Move and select within a group. |
Accessibility
- Each group is a
radiogroupnamed by its visible label; sold-out options say so in their name. - The stock line is a polite live region, so a change of size announces "Only 2 left in Clay, S".
Motion
The swatch ring and the size outline are shared-layout indicators on the snappy spring, interruptible mid-glide. A sold-out size's line wipes in from the left over 280ms and wipes back out to the right in 180ms. The price rolls its digits when the variant's price differs; the colour name and stock line swap with blur. Reduced motion: indicators jump and lines fade.
Notes for AI
Pick it for the product page's buy box. Feed it every variant with live stock; don't pre-filter sold-out ones, the picker shows them struck.
Don't build your own price or stock text next to it: it renders both. Pair it with one primary Add to bag button that changes to "Notify me" when the selected variant has no stock.
Related
- Product Card: quick add from a grid.
- Cart Drawer: where the variant lands.