# Variants and the configurator

What you picked under **What are you selling?** decides which extra cards the product page carries. A **Product** gets the **Options & variants** card; a **Made to measure** product gets the three configurator cards (**Dimensions**, **Price rules**, and **Option groups**). These are cards on the same product page, not separate tabs or screens, and they save as you go rather than through the header **Save**. Everything here builds on [Managing products](/docs/products/index).

## Options & variants

Shown only for products created as **Product**. Options are optional: a product without them is sold at its own price.

### Define options

A product can have up to 3 options. Each option row has an **Option name** (e.g. Size) and comma-separated **Values** (e.g. `S, M, L`). Edit the rows and click **Save options** ("Options saved" confirms it).

### Generate variants

1. Click **Generate variants**. The card builds every missing combination of your option values as draft rows; combinations that already exist as variants are skipped.
2. On each draft row, set the **SKU** and **Price**.
3. Click **Save {count} variants** to create them, or **Drop** a row you do not want.

### Manage existing variants

The variants table shows each variant with these columns:

| Column | Description |
| --- | --- |
| Variant | The option values joined with " / " (e.g. `S / Red`). |
| SKU | The variant's SKU. |
| Price | The variant's own price, or "(inherited)" when it is empty and the product's default price applies. |
| Stock | Per-variant stock quantity input with a **Save** button (Enter also saves). 0 or more, with at most 3 decimals and a dot or a comma. Only shown when the Stock module is enabled. |
| Active | Whether the variant is purchasable, with an **Activate** / **Deactivate** toggle. |

**Delete** removes a variant after a confirmation: 'Delete variant "{label}"? Its stock level is removed with it.' Deleting a variant deletes its stock level along with it.

## The configurator

Shown only for products created as **Made to measure**. The configurator is a module of its own, **Configurator**, enabled per business by your platform admin. While it is on, **Made to measure** is offered when you create a product and these cards appear on its page. While it is off, the cards are hidden, the product page shows "Made to measure is turned off; this product cannot be bought.", and storefronts can neither configure nor buy such a product.

A made-to-measure product is priced in one of two ways. Without dimensions, the product's **Base price** is the starting point and option choices adjust it (a console with storage and colour options, a menu with add-ons). With up to 2 numeric dimensions (like width and height), a grid of price rules over those dimensions replaces the base price, and option choices adjust the grid price (made-to-measure products). The floating **Product preview** on the detail page renders the live configurator so you can test the result as a customer would see it.

### Dimensions

The **Dimensions** card holds up to 2 dimensions. Each has:

| Field | Description |
| --- | --- |
| Key | The technical identifier for the dimension. |
| Label | What the customer sees (e.g. Width). |
| Unit | The unit shown next to the input (e.g. cm). |
| Min | The smallest value a customer can enter. |
| Max | The largest value a customer can enter. |

Use **Add dimension** to add a row and the remove button to drop one, then click **Save dimensions** ("Dimensions saved").

Renaming a dimension's key while keeping the axis carries its per-choice constraints across with it.

If any option choice on the product is priced **per m²** and still **Active**, saving fewer than two dimensions is refused, naming those choices: area pricing needs both axes. Delete or deactivate them first. Reactivating such a choice while the product has one axis is refused for the same reason; editing it while it stays inactive is fine.

Changing the *number* of dimensions deletes every price rule on the product. The dialog warns "Changing the number of dimensions deletes all {count} price rule(s) for this product. Proceed?" and the confirm button reads **Save and delete rules**. Editing a dimension's values without changing the count keeps your rules.

#### Constraints

Each saved dimension has a **Constraints** dialog. A constraint row pairs a **Choice** (from your option groups) with a **Min** and/or **Max** that narrow the dimension's range when that choice is selected. Each row needs at least one bound, min must not exceed max, and each choice can have only one constraint per dimension. Saving confirms with "Constraints saved."

### Price rules

The **Price rules** card appears once at least one dimension exists. Prices are organized into matrices, shown as tabs across the top of the card. A matrix with no conditions is labeled **Default**.

- **Add matrix** creates a new matrix with a name and a conditions picker of option choices. The matrix "Applies when all selected choices are chosen", so you can price, say, a premium material differently across the whole grid.
- Each matrix has an actions menu with **Rename**, **Edit conditions**, and **Delete matrix** (the confirmation includes how many rules it holds).
- The conditions picker refuses an **inactive** choice: a matrix conditioned on one can never be selected, so its prices would never apply. Activate the choice or pick another.

#### Bands and the price grid

Within a matrix, each dimension gets a band editor: **From**-**To** pairs that slice the dimension's range. Use **Add band** to add one; removing a band that already holds typed prices asks for confirmation. A **Generate bands** helper builds evenly stepped bands from **From**, **To**, and **Step** values (whole numbers only).

The price grid is a table with dimension 1 bands as rows and dimension 2 bands as columns (a single row when there is only one dimension). Type a price into each cell. You can paste a block of cells straight from a spreadsheet (tab- or newline-separated); if any pasted token is not a valid price, the whole paste is rejected.

**Save price rules** validates every band and every cell before saving ("Price rules saved"). Grids larger than 400 cells show a warning that the editor may get slow.

If the rules stored on the product do not form a grid (for example, rules created through the API), the card switches to a flat list editor with the notice "These rules don't form a grid — edit as list." Each rule row then has min/max per dimension plus a price, with **Add price rule**, remove, and **Save price rules**.

### Option groups

The **Option groups** card is an accordion of groups, each holding choices the customer picks in the storefront configurator. Unlike dimensions and price rules, this card saves automatically: every field saves on blur or toggle, a green tick confirms the save, and the value reverts (with an error toast) if it fails. There is no Save button here.

Groups and choices are shown in the order the storefront presents them. Use the **Move up** / **Move down** arrows on a group header or a choice row to change that order; the new order saves immediately and confirms with "Order saved." The number in front of each group name is its position.

Per group you can edit:

| Field | Description |
| --- | --- |
| Name | The group's label. |
| Type | **Single choice** or **Multiple choice**. |
| Required | Whether the customer must pick something in this group. |

Use the **Add group** row (name, type, required) to create a group, and **Delete group** to remove one (the confirmation includes its choice count).

Per choice, each row has:

| Field | Description |
| --- | --- |
| Name | The choice's label. |
| Delta type | How the choice changes the price: **Fixed amount**, **Per area**, **Percent of base**, or **Percent of total**. |
| Value | The price or percentage for the delta. |
| Component product | Another product this choice consumes as a component. |
| Qty | How many units of the component product, greater than 0 with at most 3 decimals (a dot or a comma, e.g. `2,16` m² of fabric). |
| Default | Whether the choice is pre-selected. |
| Active | Whether the choice is offered at all. Turning it off also hides every other choice whose **Requires** rule pointed only at this one, so the storefront never shows an option that can no longer be picked. |
| Quote only | When on, selecting this choice forces the order into a quote instead of direct checkout. This is per-choice; it is separate from the product-level **Purchasability** setting of "Quote only". It only takes effect once quote requests are enabled under [Sales → Settings → Quotes](/docs/sales/settings#quote-requests); until then the card shows a hint. |

Each choice also has three actions:

- **Rules** opens a dialog of rows pairing **Requires** or **Excludes** with a target choice: "Control when this choice is available. Requires: at least one selected per group. Excludes: cannot combine." Saving confirms with "Rules saved."
- **Images** opens a dialog to pick ordered images from the media library; see [Media](/docs/products/media).
- **Delete choice** removes the choice after confirmation.

**Deactivating a choice that other choices require.** A **Requires** rule is read as "at least one target selected per group", so turning **Active** off on one of two alternatives in the same target group changes nothing. Turning off the last active target in a group withdraws the choice that required it: the storefront stops offering it and can no longer select it, even though that choice is still switched **Active** here. The effect chains, so a choice that required the withdrawn one is withdrawn too, and a pair of choices that require each other is unaffected. Reactivating the target brings all of them back, and the rules themselves are never touched. **Delete choice** behaves differently on purpose: a deleted target takes its rules with it, so the choices that required it stay on offer. The card flags a withdrawn choice for you: a warning pill under its row reads "Hidden from storefront: requires Group: Choice, which is inactive." (or "which is itself hidden" when the named target was withdrawn the same way), and a **Required** group with nothing available left is flagged on its header with "Nothing available in this required group: customers cannot configure this product." The **Rules** dialog suffixes an inactive target with "(inactive)" so you can see what you are pointing a rule at; picking one stays allowed, because it is reversible.

Use the add-choice row at the bottom of each group to add new choices.

For how a storefront drives the configurator and fetches price quotes, see the [configurator API reference](/api/configurator/index).
