skills/saleor/storefront/variant-selection

variant-selection

SKILL.md

Variant Selection System

Source: Saleor Docs - Attributes - How product/variant attributes work

When to Use

Use this skill when:

  • Modifying variant/attribute selection on product pages
  • Understanding why a variant isn't selectable
  • Adding discount indicators to variant options
  • Debugging "Add to Cart" button state

Instructions

Core Concept: Variants, Not Products

You add VARIANTS to cart, not products. Each variant is a specific attribute combination:

Product Attributes Variant ID
T-Shirt Black + Medium abc123
T-Shirt Black + Large def456
T-Shirt White + Medium ghi789

The checkoutLinesAdd mutation requires a specific variantId. Without selecting ALL attributes, there's no variant to add.

Two Types of Variant Attributes

Saleor distinguishes between two types of variant attributes:

Type variantSelection Purpose UI Passed to Cart?
Selection VARIANT_SELECTION Identify which variant (color, size) Interactive picker No - just the variantId
Non-Selection NOT_VARIANT_SELECTION Describe the variant (material, brand) Display-only badges No - already on variant

Key insight: Neither type is "passed" to checkout. You only pass the variantId. All attributes are already stored on the variant in Saleor.

# GraphQL queries use the variantSelection filter:
selectionAttributes: attributes(variantSelection: VARIANT_SELECTION) { ... }
nonSelectionAttributes: attributes(variantSelection: NOT_VARIANT_SELECTION) { ... }

Non-selection attributes are display-only - shown as informational badges, not interactive selectors.

File Structure

src/ui/components/pdp/variant-selection/
├── index.ts                      # Public exports
├── types.ts                      # TypeScript interfaces
├── utils.ts                      # Data transformation & logic
├── variant-selector.tsx          # Single attribute selector
├── variant-selection-section.tsx # Main container
├── optional-attributes.tsx       # Non-selection attribute badges
└── renderers/
    ├── color-swatch-option.tsx   # Color swatch (circular)
    └── button-option.tsx         # Button for size/text (unified)

Key Functions in utils.ts

Function Purpose
groupVariantsByAttributes() Extract unique attribute values from variants
findMatchingVariant() Find variant matching ALL selected attributes
getOptionsForAttribute() Get options with availability/compatibility info
getAdjustedSelections() Clear conflicting selections when needed
getUnavailableAttributeInfo() Detect dead-end selections

For detailed function signatures and usage, see UTILS_REFERENCE.md.

Option States

State Meaning Visual Clickable?
Available In stock Normal
Incompatible No variant with this + current selections Dimmed ✓ (clears others)
Out of stock Variant exists but quantity = 0 Strikethrough

URL Parameter Pattern

Selections are stored in URL params:

?color=black&size=m&variant=abc123
  ↑           ↑       ↑
Color sel  Size sel  Matching variant (set automatically)

The variant param is only set when ALL attributes are selected.

Discount Badges

Options can show discount percentages:

// In utils.ts
interface VariantOption {
	id: string;
	name: string;
	available: boolean;
	hasDiscount?: boolean; // Any variant with this option is discounted
	discountPercent?: number; // Max discount percentage
	// ...
}

The renderers display a small badge when discountPercent is set.

Examples

Smart Selection Adjustment

When user selects an incompatible option:

State: ?color=red (Red only exists in Size S)
User clicks: Size L
Result: ?size=l (Red is cleared, not blocked)

Users are never "stuck" - they can always explore all options.

Dead End Detection

const deadEnd = getUnavailableAttributeInfo(variants, groups, selections);
// Returns: { slug: "size", name: "Size", blockedBy: "Red" }
// UI shows: "No size available in Red"

Custom Renderers

<VariantSelectionSection
	variants={variants}
	renderers={{
		color: MyCustomColorPicker,
		size: MySizeChart,
	}}
/>

State Machine

The selection system has 5 states with automatic conflict resolution. For the full state diagram and transition rules, see STATE_MACHINE.md.

Quick reference:

State Add to Cart Description
Empty No selections
Partial Some attributes selected
Complete All selected, variant found
Conflict Auto-clears to Partial
DeadEnd Selection blocks other groups

Key behavior: When user selects an incompatible option, other selections are cleared automatically (not blocked). Users can always explore all options.

Anti-patterns

Don't enable "Add to Cart" without full selection - Needs variant ID
Don't block incompatible options - Let users click, clear others
Don't assume single attribute - Products can have multiple
Don't use 0 in boolean checks for prices - Use typeof === "number"
Don't make non-selection attributes interactive - They're display-only (badges, not toggles)

Weekly Installs
3
GitHub Stars
1.4K
First Seen
Feb 19, 2026
Installed on
amp3
github-copilot3
codex3
kimi-cli3
gemini-cli3
cursor3