# Product configurator

> Prices calculated from the customer's choices with a formula, such as a price per letter, per square metre or per material, shown on the page and charged at checkout.

Status: Ready (Smootify 2.0)  
Plan: Server plan  
Add-on: Product configurator  
1.x page: https://docs.smootify.io/docs/ecommerce/add-ons/product-configurator  
URL: https://www.smootify.io/docs/features/product-configurator  
Updated: 2026-10-10

The product configurator calculates the price from what the customer chooses: 30 cents per engraved letter, a price per square metre, a wood that costs 40% more. Each choice is a form field with a small formula. The price on the page follows the choices, and the customer pays that price at checkout. It covers what Shopify variants cannot: free sizes, long lists of options, extras that add up.

> [!NOTE]
> The configurator needs the **server** plan and **Product configurator** turned on in the Smootify dashboard. Without them, the configurator fields are removed from the page and `configurator-share` does nothing. It runs on the site's licensed domain. On your `.webflow.io` address it runs once the site is paid, not during a trial.

## How it fits together

What Smootify needs:

- the configurator fields inside the Form of the product’s `smootify-add-to-cart`;
- one Custom element per field, with one input inside;
- a formula for each field, or for each choice of a field.

The price starts from the price of the selected variant. Each field that is **on** (filled in, checked, chosen) applies its formula, in the order the fields appear on the page, and passes the result to the next one. Here, with a variant at $120.00 per square metre and the fields of step 2:

| Step | Price |
|---|---|
| the variant price: `price` starts here | $120.00 |
| `configurator-dimension`, `price * width * height`, with 1.5 × 0.8 m | $144.00 |
| `configurator-select`, the choice `Walnut\|price * 1.4` | $201.60 |
| `configurator-checkbox`, `price + 5`, not checked: off, so skipped | $201.60 |
| `smootify-price`, `data-prop="price"` | $201.60 |
| `smootify-price`, `data-prop="total"`: the price times the quantity, 2 | $403.20 |

Each field is a Custom element with one of these tags:

| Element | Put inside | On while | Formula |
|---|---|---|---|
| [`configurator-input`](https://www.smootify.io/docs/reference/elements/configurator-input) | a Text field or a Text area | it holds a valid value | `data-formula`, which can use `length` |
| [`configurator-checkbox`](https://www.smootify.io/docs/reference/elements/configurator-checkbox) | a Checkbox | it is checked | `data-formula` |
| [`configurator-radio`](https://www.smootify.io/docs/reference/elements/configurator-radio) | Radio buttons | a radio is chosen (clicking it again unchecks it) | in each radio's Choice value, after a `\|` |
| [`configurator-select`](https://www.smootify.io/docs/reference/elements/configurator-select) | a Select field | a choice with a value is chosen | in each option's value, after a `\|` |
| [`configurator-dropdown`](https://www.smootify.io/docs/reference/elements/configurator-dropdown) | a Webflow Dropdown and a Select field | a choice is made | in the options of the Select field, after a `\|` |
| [`configurator-popover`](https://www.smootify.io/docs/reference/elements/configurator-popover) | a popover and a Select field | a choice is made | in the options of the Select field, after a `\|` |
| [`configurator-dimension`](https://www.smootify.io/docs/reference/elements/configurator-dimension) | Text fields with the Type Number | every one holds a valid number | `data-formula`, which can use each field's Name |
| [`configurator-file-input`](https://www.smootify.io/docs/reference/elements/configurator-file-input) | an upload area | at least one file is uploaded | `data-formula`, which can use `file` |
| [`configurator-field`](https://www.smootify.io/docs/reference/elements/configurator-field) | configurator fields, to group them | never: it only groups | a `data-formula` on it does nothing |

What is up to you: which fields a product uses, their order, labels, wrappers and styles.

Each field’s value is also saved on the cart line under the field’s Name, like any other field of the form, so the merchant sees the choices on the order.

## 1. Write the formulas

A formula is a calculation that gives the new price. It can use:

- `price`: the price so far, starting from the variant price;
- `quantity`: the quantity in the form;
- `length`: in a `configurator-input`, the number of characters typed;
- in a `configurator-dimension`, the Name of each number field, such as `width` and `height`;
- `file`: in a `configurator-file-input`, the number of files uploaded.

| Formula | Price |
|---|---|
| `price + 5` | 5 more |
| `price + 0.3 * length` | 0.30 more per character |
| `price * width * height` | the variant price per square metre |
| `price * 1.4` | 40% more |
| `max(price * width * height, 50)` | per square metre, at least 50 |

**Where the formula goes.** On a field with one value, in the custom attribute `data-formula`. On a field with choices (radio, select, dropdown, popover), in the value of each choice, after a `|`: `Walnut|price * 1.4` is the choice Walnut, with its formula. Write it in the **Choice value** setting of a Radio button, or in the value of a Select field option. The customer and the order see only `Walnut`.

**How formulas are read.**

- `+`, `-`, `*`, `/` and `^` (power) work as in maths, and so do functions such as `round(…)`, `ceil(…)`, `min(…, …)` and `max(…, …)`.
- `%` is the remainder of a division, not a percentage. A `%` at the end of a number is ignored, so `price * 10%` multiplies by 10. For 10% more, write `price * 1.1`.
- A name that does not exist counts as 0.
- A formula that cannot be read changes nothing. A result that is not a number, such as a division by 0, keeps the price as it was.

The formula builder writes a formula from a field and a rule in words, tries it with a price, and warns about names that count as 0 and about `%`:

_The formula builder of this page writes a formula from a field and a rule, and tests it with a price: open the page in a browser to use it. The rules it writes:_

| Rule | Formula |
|---|---|
| add an amount, such as 5 | `price + 5` |
| an amount per character of a `configurator-input` | `price + 0.3 * length` |
| an amount per file of a `configurator-file-input` | `price + 2 * file` |
| the variant price per unit of area of a `configurator-dimension` | `price * width * height` |
| an amount per unit of area | `price + 40 * width * height` |
| a percentage more, such as 40% | `price * 1.4` |
| a percentage less, such as 10% | `price * 0.9` |
| a fixed price | `25` |
| at least a price, such as 50 | `max(…, 50)` around the formula |
| rounded to a whole number, or up | `round(…)`, `ceil(…)` around the formula |

On a field with choices, each choice is its name, a `|` and its formula: `Walnut|price * 1.4`. A choice without a formula keeps the price.

## 2. Build the fields

```html
<configurator-dimension data-formula="price * width * height">
  <div><label>Width (m)</label><input type="number" name="width" min="0.5" max="3" step="0.01"><error-message>From 0.5 to 3 m</error-message></div>
  <div><label>Height (m)</label><input type="number" name="height" min="0.5" max="3" step="0.01"><error-message>From 0.5 to 3 m</error-message></div>
</configurator-dimension>
<configurator-select>
  <select name="wood"><option value="">Choose a wood</option><option value="Oak|price">Oak</option><option value="Walnut|price * 1.4">Walnut</option></select>
</configurator-select>
<configurator-checkbox data-formula="price + 5">
  <label class="w-checkbox"><input type="checkbox" name="gift-wrap" class="w-checkbox-input"><span class="w-form-label">Gift wrap (+ $5)</span></label>
</configurator-checkbox>
<smootify-price><div data-prop="price">$0.00</div><div data-prop="total">$0.00</div></smootify-price>
```

- **One input per element.** Each Custom element handles one input. For three questions, use three elements.
- **One Name per field.** Give every field its own Name in its settings. Two fields with the same Name count as one.
- **Dimensions.** Only Text fields with the Type Number count; Smootify removes the others. Their Names are the names in the formula, so use one plain word, such as `width`. `min`, `max` and `step` custom attributes set what is valid; for decimals, set `step`, such as `0.01`.
- **Preselected values count.** A value a field already has when the page opens is in the price from the start: a `value`, a checked checkbox or radio, a `selected` option set in the Designer, and what the browser puts back on Back. For a step that should start off, leave it empty and unchecked.
- **Numbers start at their minimum.** An empty Number field with `min`, in `configurator-dimension` or `configurator-input`, starts at its minimum, and the price with it: a desk from 100 × 60 cm opens at 100 × 60, priced. Its placeholder shows only if the customer empties it.
- **Radios.** Clicking the chosen radio again unchecks it and turns its formula off.
- **Dropdown and popover.** The Select field inside holds the choices and their formulas, and Smootify hides it. The first Dropdown link, or the first link or button inside the popover, is the template of every choice. See [popovers](https://www.smootify.io/docs/build/popovers).
- **Files.** `configurator-file-input` is an upload area built like the [file uploads](https://www.smootify.io/docs/features/file-uploads). Its files are saved on the line like a `file-uploader`’s.

**Error messages.** A Custom element with the tag `error-message` shows only while its field is not valid, for example when the customer tries to add a size out of range. Put it anywhere inside `configurator-input` or `configurator-radio`. In `configurator-dimension`, put it right after its number field. The element and the field also get the class `is-invalid`. The browser shows no message of its own, so every field with limits or a required value needs its error state, with a text that says the limits, such as “Between 60 and 90 cm”: without it, Add to cart stops and the customer does not know why, or what to type.

```html
<configurator-input data-formula="price + 0.3 * length">
  <label>Engraving</label>
  <input name="engraving">
  <error-message>Write the text to engrave</error-message>
</configurator-input>
```

**Required fields.** Turn on **Required** in a field’s settings (on a field inside a component, where Webflow cannot keep it, give the input the custom attribute `data-required`; `False` turns it off), with `min` and `max` on a size, so that Add to cart and Buy now stop on an empty field and the browser points to it. On a product template that every product shares, put the configurator in a block shown only on the products that have one, such as one with `if-metafield`: while a condition hides the block, its required fields do not count, and the other products add to the cart as usual.

## 3. Prices by quantity

A price table by quantity, such as a lower price per item from 10 pieces, goes inside the `quantity-input` of the form, as a Code Embed that the tool below copies for you. Each row applies from its quantity, included, up to the next row: with rows at 10 and 50, one item costs the first price from 10 to 49 pieces and the second from 50 on. Below the first row, the variant price applies.

The price of the row replaces the variant price, then the field formulas apply. It is charged even when no field changes the price. Build the table, copy it, and paste it in the Designer with `quantity-input` selected:

_The prices-by-quantity tool of this page builds a Webflow Code Embed from a table of quantities and prices, copied as a Webflow element: open the page in a browser to use it._ Each row is a quantity and the price of one item from that quantity on, such as 10 → 12.50 and 50 → 10.00: from 10 to 49 items one costs 12.50, from 50 on 10.00, below 10 the variant price. Paste it inside the `quantity-input` of the form.

## 4. On the page and at checkout

A `smootify-price` in the product shows the configured price in `data-prop="price"`, and the price times the quantity in `data-prop="total"`.

**Add to cart.** The line goes to the cart with the configured price. When the fields leave the price as it was, the line is an ordinary one.

**Checkout.** A cart with a configured price checks out through a Shopify draft order: the customer pays on the draft order’s invoice page, not on the usual checkout. Every line of the cart goes into that order, except subscription lines. The cart is emptied when the customer leaves for the invoice.

**Buy now.** A Custom element with the tag `button`, `type="submit"` and `name="buy-now"` goes straight to the invoice for this product and its add-ons. The rest of the cart stays as it was.

The buy-again button of the [order pages](https://www.smootify.io/docs/build/orders) leaves configured products out: a cart cannot carry their price.

**Show the choices in the page.** A summary such as “How your price is made” can follow what the customer chooses, as they type. Inside the `smootify-product` of the form, add these custom attributes to text elements. A field is named by its **Name** in Webflow’s settings, such as `Engraving` or `Gift wrap`; for a dimension, by the Name of one of its number fields.

| Custom attribute | Shows |
|---|---|
| `form-value="Engraving"` | what the customer wrote or chose: the text, the label of the chosen option or of a checked box, the names of the files |
| `form-value="Engraving"` + `data-variable="length"` | the value the formula reads, such as the number of characters (`file` for the number of files) |
| `configurator-price="base"` | the price of the chosen variant |
| `configurator-price="Engraving"` | what that choice adds to the price |
| `configurator-price="total"` | the configured price of one item |
| `configurator-formula="width * depth / 10000"` | a calculation with the same names as the formulas, with `price`, `base` and `quantity`; `data-digits` sets the decimals (2 by default) |
| `if-configurator="Engraving"` | shown only while that choice counts: filled, checked or chosen, and not hidden by a condition. `unless-configurator` is the opposite |

```html
<div>Top <span configurator-price="base">€ 0.00</span></div>
<div if-configurator="width">Surface <span form-value="width">0</span> × <span form-value="depth">0</span> cm = <span configurator-formula="width * depth / 10000">0</span> m² <span configurator-price="width">€ 0.00</span></div>
<div if-configurator="Engraving">Engraving “<span form-value="Engraving"></span>”, <span form-value="Engraving" data-variable="length">0</span> characters <span configurator-price="Engraving">€ 0.00</span></div>
<div>Your desk <span configurator-price="total">€ 0.00</span></div>
```

**Before the customer chooses.** Add `data-fallback` with a text to show in its place: on `form-value`, while the field is empty (with `data-variable`, also while the field does not count in the price); on `configurator-price="Engraving"`, while that choice does not count (without it, the price shows 0); on `configurator-formula`, while one of its names has no value yet. `base` and `total` always have a value. An empty `data-fallback` leaves the element empty, and a row hidden by `if-configurator` stays hidden.

**Two fields, one Name.** On a template every product shares, two configurator fields can have the same Name, such as the engraving of a desk and of a shelf in blocks shown on different products. Each counts on its own, and the summary reads the one on show.

The same `form-value` shows the answers of [dynamic properties](https://www.smootify.io/docs/features/dynamic-properties).

## 5. Save and share a configuration

A customer who configures a desk rarely buys it on the first visit. `configurator-share` gives them a link to the same configuration, to open again on another device or to send to someone else.

Add a Custom element with the tag `configurator-share` inside the product’s `smootify-add-to-cart`, with a Custom element with the tag `button` inside. A click builds a link to this page with every choice in it, one parameter per field under its Name (`?Width=180&Finish=walnut`), plus the variant when the product has more than one. On phones it opens the share sheet; elsewhere it copies the link. With `data-copy` it always copies. `data-title` is the title of the share. Style the moment with the combo classes `is-copied` or `is-shared`, which last two seconds.

```html
<smootify-add-to-cart>
  <div class="w-form">
    <form>
      <configurator-field>…</configurator-field>
      <configurator-share data-title="My desk"><button type="button">Share this desk</button></configurator-share>
    </form>
  </div>
</smootify-add-to-cart>
```

Whoever opens the link sees the fields already filled, as if they had typed and chosen them, and the price that follows. A value a field would refuse stays off, such as a width below its `min` or an option the select no longer has. A file the customer uploaded is not in the link: it is uploaded again.

To keep a configuration in your store instead, with your own price check, use a quote: the cart’s quote button turns the configured lines into a draft order. See [Quotes](https://www.smootify.io/docs/features/quotes).

## Before you publish

Test an order on the licensed domain, from the product page to the invoice page. The invoice page already has the discount codes of the cart, and accepts more codes and gift cards. It is not the usual checkout: check the payment methods the customer sees there.

> [!STUCK]
> - **The fields disappear.** The site is not on the server plan, or Product configurator is not turned on.
> - **The price does not change on `.webflow.io`.** During a trial, server add-ons run only on the licensed domain.
> - **The price does not change on a gift card.** Shopify issues a gift card for its variant's value whatever was paid, so the configurator leaves a gift card product's price alone. For a gift card at any amount, see [Name your price](https://www.smootify.io/docs/features/name-your-price#a-gift-card-of-any-amount).
> - **A formula does nothing.** It is on `configurator-field`, it cannot be read, or its field is not on (empty, unchecked, invalid).
> - **The price is 10 times too high.** The formula uses `%` as a percentage. Write `price * 1.1` for 10% more.
> - **A size field is never valid.** The number has decimals and the field has no `step`, or the value is outside `min` and `max`.
> - **Add to cart does nothing on a configurable product.** A field is out of range or empty and the page has no `error-message` or `is-invalid` style to say so.
> - **An error message always shows.** In `configurator-dimension`, it is not right after its number field.
> - **A product without a configurator cannot be added to the cart.** Its required fields are in a block hidden with CSS or `display: none`. Hide the block with a condition, such as `if-metafield`, or with Webflow's conditional visibility: hidden that way, the fields stop being required.
> - **Two choices count as one.** The fields share the same Name.
> - **The shared link opens with empty fields.** The fields have no Name, or a different one from when the link was made: the link names each choice after its field.

## Coming from 1.x

- Formulas no longer need square brackets: `price + 1` and `[price] + 1` give the same result.
- A row of the prices by quantity applies from its quantity: a 1.x row at 10 started at 11. To keep the 1.x thresholds, build the table again with each quantity one higher.
- The **Draft Orders** add-on is no longer needed for the configurator.
- `configurator-file-input` uses the upload markup of [file uploads](https://www.smootify.io/docs/features/file-uploads) instead of the 1.x upload widget.
- Buy now with a configured price no longer empties the cart, and the formulas of radios and selects work without a `data-formula` on the element.
