VariantSelector
Every dimension of a product, each as the selector its options call for.
import { VariantSelector } from "@evolve-storefront/feature-product/variant-selector";Renders inside ProductListingProvider.
Preview
Usage
<VariantSelector dimensions={product.variantDimensions} toHref={({ slug, sku }) => productDetailHref(slug, sku)} renderLink={({ children, ...props }) => <Link {...props}>{children}</Link>} renderImage={(props) => <Image {...props} width={64} height={64} />}/>;Props
| Prop | Type | Description |
|---|---|---|
toHref | (target: VariantTarget) => string | The app's product route: |
dimensions | readonly VariantDimension[] | |
renderLink? | ((props: VariantLinkProps) => ReactNode) | |
renderHelp? | ((dimension: VariantDimension) => ReactNode) | Shown under the legend, such as a size chart's trigger or a fit line. |
renderImage? | ((props: VariantImageProps) => ReactNode) |
Any other props go to the root element, like on a plain HTML element.
Change the layout
Pass children to VariantSelector to arrange its parts yourself. Every part is exported from @evolve-storefront/feature-product/variant-selector.
ButtonSelectorA dimension's options as labelled buttons, such as sizes.
| Prop | Type | Description |
|---|---|---|
dimension | VariantDimension | |
toHref | (target: VariantTarget) => string | The app's product route: |
dimensions? | readonly VariantDimension[] | Every dimension of the product, so an option's |
renderLink? | ((props: VariantLinkProps) => ReactNode) | |
renderHelp? | ((dimension: VariantDimension) => ReactNode) | Shown under the legend, such as a size chart's trigger or a fit line. |
Any other props go to the root element, like on a plain HTML element.
ChooseVariantButtonStands in for the add button while a dimension is unchosen ("Choose your size").
| Prop | Type | Description |
|---|---|---|
dimension | Pick<VariantDimension, "label" | "name"> | The dimension still to choose: the first of |
variant? | ButtonVariant | |
tone? | ButtonTone | |
isIconOnly? | boolean | The button has no visible label, so it is square. Structural rather than a size: it says the button has nothing to be wide for, and says nothing about how big — the brand's control tokens still decide that. MoreNot inferred from the children. A kebab menu is as often a glyph or an SVG
as an |
asChild? | boolean | |
isPending? | boolean | The action is running. The button keeps its label, so its width and
accessible name hold, shows a spinner and is |
data-component? | undefined | A button's identity is not a caller's to rename — see |
Any other props go to the root element, like on a plain HTML element.
ColorSelectorA colour dimension as swatches, coloured by each option's color only.
| Prop | Type | Description |
|---|---|---|
dimension | VariantDimension | |
toHref | (target: VariantTarget) => string | The app's product route: |
dimensions? | readonly VariantDimension[] | Every dimension of the product, so an option's |
renderLink? | ((props: VariantLinkProps) => ReactNode) | |
renderHelp? | ((dimension: VariantDimension) => ReactNode) | Shown under the legend, such as a size chart's trigger or a fit line. |
Any other props go to the root element, like on a plain HTML element.
ImageSelectorA dimension as images of its variants, falling back to the label where one has none.
| Prop | Type | Description |
|---|---|---|
dimension | VariantDimension | |
toHref | (target: VariantTarget) => string | The app's product route: |
dimensions? | readonly VariantDimension[] | Every dimension of the product, so an option's |
renderLink? | ((props: VariantLinkProps) => ReactNode) | |
renderHelp? | ((dimension: VariantDimension) => ReactNode) | Shown under the legend, such as a size chart's trigger or a fit line. |
renderImage? | ((props: VariantImageProps) => ReactNode) |
Any other props go to the root element, like on a plain HTML element.
Examples
Choose A SizeNo size until the shopper picks one: variantSelection clears the server's default, and ChooseVariantButton stands in for the add button.
const { choice, sku } = await readVariantChoiceForProduct(serverClient, { slug, params });const product = await getProduct(slug, sku); // variantDimensions(selectedSku: sku)const { dimensions, unchosen } = variantSelection(product.variantDimensions, { choice });
<VariantSelector dimensions={dimensions} toHref={({ slug, search }) => `${productDetailHref(slug)}?${search}`} renderLink={(props) => <Link {...props} />} renderHelp={(dimension) => dimension.name === "size" ? <SizeChartSheet /> : null}/>{unchosen[0] ? <ChooseVariantButton dimension={unchosen[0]} /> : <AddToCartButton sku={sku} />}Price DifferencesOptions that cost different amounts show the difference against the selected one, and with the cheapest selected a line names the next one up.
<VariantSelector dimensions={product.variantDimensions} // options select variant { price { gross } } toHref={({ slug, search }) => `${productDetailHref(slug)}?${search}`}/>Sold Out ChosenA sold-out size the shopper has chosen: struck through on the chosen fill, in the chosen state's own text colour so it keeps its contrast.
<VariantSelector dimensions={product.variantDimensions} // options select variant { stock { level } } toHref={({ slug, search }) => `${productDetailHref(slug)}?${search}`}/>Fit From ReviewsHow the size fits by the reviews, under the size legend: ProductReviewsFit in renderHelp, fed the product's reviewFitDistribution.
<VariantSelector dimensions={product.variantDimensions} toHref={({ slug, search }) => `${productDetailHref(slug)}?${search}`} renderHelp={(dimension) => dimension.name === "size" ? ( <ProductReviewsFit fitDistribution={product.reviewFitDistribution} /> ) : null}/>
