When launching an auto parts online store, we faced a problem: the standard 1C-Bitrix catalog did not allow the customer to assemble an engine from individual modules with compatibility verification. Each module had its own options, and combinations were interdependent. Customers got confused in compatibility tables, and managers spent hours on manual selection. The solution—a configuration calculator on Bitrix with a dependency graph. The user selects options, the system checks compatibility in real time, shows the price, and adds the assembled set to the cart.
We, a team of certified developers with 10 years of experience, implement such configurators. Our approach guarantees reliable operation even with 50+ options. Relevant for auto dealers, furniture manufacturers, IT integrators, and industrial equipment suppliers. Order development—we will assess the project for free.
What is the difference from standard trade offers?
The standard catalog uses trade offers (SKUs) with fixed attribute combinations. Our configurator performs dynamic assembly: selecting the "Lux" package includes options A, B, C and blocks D. The final price is summed from components, not taken from an SKU. The result is a set of products for the cart. Thanks to the dependency graph, the configurator works 2 times faster than standard solutions.
How is the data model structured?
We use the HL-block ConfiguratorComponents to store options:
| Field | Type | Description |
|---|---|---|
| UF_PRODUCT_ID | Int | ID of the base product in the catalog |
| UF_GROUP_CODE | String | Option group (engine, color, wheels) |
| UF_OPTION_CODE | String | Option code |
| UF_OPTION_NAME | String | Display name |
| UF_BASE_PRICE | Float | Option price |
| UF_PRICE_TYPE | Enum | fixed / percent / delta |
| UF_INCOMPATIBLE | String | Codes of incompatible options (comma-separated) |
| UF_REQUIRED_WITH | String | Options required when this one is selected |
HL-block ConfiguratorPresets—ready-made configurations (Basic, Standard, Lux):
| Field | Type | Description |
|---|---|---|
| UF_PRODUCT_ID | Int | Base product |
| UF_PRESET_CODE | String | Preset code |
| UF_PRESET_NAME | String | Name |
| UF_OPTIONS_JSON | Text | JSON with selected options |
| UF_DISCOUNT_PERCENT | Float | Discount on the preset |
Why is the dependency graph critical for performance?
With 50+ options, manual testing is impossible. The dependency graph allows instant determination of incompatibilities and required options. Each selection triggers a price recalculation and blocking of non-applicable variants. We build the graph on the JS side to offload the server. As a result, the user sees updates without delays. 1C-Bitrix documentation recommends this approach for complex configurators.
How to manage option dependencies?
The central problem of the configurator is synchronizing dependencies. When an option is selected, we need to: remove incompatible options from other groups, automatically add required related ones, and recalculate the total price.
class ProductConfigurator {
constructor(basePrice, components) {
this.basePrice = basePrice;
this.components = components;
this.selected = {};
this.graph = this.buildIncompatibilityGraph();
}
buildIncompatibilityGraph() {
const graph = {};
this.components.forEach(c => {
if (c.uf_incompatible) {
graph[c.uf_option_code] = c.uf_incompatible.split(',').map(s => s.trim());
}
});
return graph;
}
selectOption(groupCode, optionCode) {
const blocked = this.graph[optionCode] || [];
Object.keys(this.selected).forEach(g => {
if (blocked.includes(this.selected[g])) delete this.selected[g];
});
this.selected[groupCode] = optionCode;
const c = this.components.find(
x => x.uf_group_code === groupCode && x.uf_option_code === optionCode
);
if (c && c.uf_required_with) {
c.uf_required_with.split(',').forEach(code => {
const req = this.components.find(x => x.uf_option_code === code.trim());
if (req) this.selected[req.uf_group_code] = req.uf_option_code;
});
}
return this.calculate();
}
calculate() {
let total = this.basePrice;
const breakdown = [];
Object.entries(this.selected).forEach(([group, code]) => {
const c = this.components.find(
x => x.uf_group_code === group && x.uf_option_code === code
);
if (!c) return;
let price = 0;
if (c.uf_price_type === 'fixed') price = c.uf_base_price;
if (c.uf_price_type === 'percent') price = this.basePrice * c.uf_base_price / 100;
if (c.uf_price_type === 'delta') price = c.uf_base_price;
total += price;
breakdown.push({ group, name: c.uf_option_name, price });
});
return { basePrice: this.basePrice, totalPrice: Math.round(total), breakdown };
}
}
Presets as an entry point
Most users do not configure from scratch. Ready-made presets serve as a starting point with the possibility of fine-tuning. A preset with a discount encourages choosing a ready package instead of manual assembly part by part. Budget savings of up to 30%—a tangible bonus.
function applyPreset(configurator, preset) {
const options = JSON.parse(preset.uf_options_json);
configurator.selected = {};
Object.entries(options).forEach(([group, code]) => {
configurator.selected[group] = code;
});
const result = configurator.calculate();
if (preset.uf_discount_percent > 0) {
result.totalPrice = Math.round(result.totalPrice * (1 - preset.uf_discount_percent / 100));
result.presetDiscount = preset.uf_discount_percent;
}
return result;
}
How to add the configuration to the Bitrix cart?
public function addToCart(int $baseProductId, array $selectedOptions): \Bitrix\Main\Result
{
$basket = \Bitrix\Sale\Basket::loadItemsForFUser(
\Bitrix\Sale\Fuser::getId(),
\Bitrix\Main\Context::getCurrent()->getSite()
);
$item = $basket->createItem('catalog', $baseProductId);
$item->setFields([
'QUANTITY' => 1,
'PROPS' => [[
'NAME' => 'Configuration',
'CODE' => 'CONFIGURATION',
'VALUE' => json_encode($selectedOptions),
]]
]);
foreach ($selectedOptions as $option) {
if (!empty($option['product_id'])) {
$optItem = $basket->createItem('catalog', $option['product_id']);
$optItem->setFields([
'QUANTITY' => 1,
'PRICE' => $option['price'],
'CUSTOM_PRICE' => 'Y',
]);
}
}
return $basket->save();
}
Saving user configuration
For authorized users, we use the HL-block SavedConfigurations:
$hash = md5($productId . json_encode($selectedOptions));
$existing = $SavedConfig::getList([
'filter' => ['=UF_USER_ID' => $USER->GetID(), '=UF_HASH' => $hash]
])->fetch();
if (!$existing) {
$SavedConfig::add([
'UF_USER_ID' => $USER->GetID(),
'UF_PRODUCT_ID' => $productId,
'UF_OPTIONS' => json_encode($selectedOptions),
'UF_PRICE' => $totalPrice,
'UF_HASH' => $hash,
]);
}
For guests, we use sessionStorage or cookies with a limited lifetime.
Step-by-step configurator implementation guide
- Create HL-blocks for options and presets.
- Fill in data—groups, options, their prices and dependencies.
- Implement a JS widget on Bitrix Framework (or a separate interface).
- Integrate with the cart via the
OnBeforeBasketAddevent. - Configure caching for fast operation with many options.
How to ensure configurator scalability?
For 100+ options, we use tagged caching. Calculation results and compatibility are cached by key configurator_{product_id}. When options change, the cache is invalidated via an agent. Queries to HL-blocks are indexed by UF_PRODUCT_ID and UF_GROUP_CODE. This guarantees response time under 100 ms even under peak loads.
What is included in the work?
- Designing the data model and dependency graph.
- Developing the frontend widget on Bitrix Framework.
- Integrating with the cart and orders.
- Saving configurations for authorized and guest users.
- API and setup documentation.
- Training administrators to work with the configurator.
- 30-day support after release.
Development timelines
| Scope | Description | Timeline |
|---|---|---|
| Basic | Up to 5 option groups, no dependencies, presets | 5–8 days |
| Standard | 5–15 groups, incompatibilities, configuration saving | 2–3 weeks |
| Complex | 15+ groups, items in cart, configuration history | 4–8 weeks |
| Production | Integration with 1C for up-to-date prices and stock | 2–4 months |
The main technical challenge is the dependency graph with a large number of options. With 50+ variants, manual testing is unrealistic: automated tests for compatibility logic are needed from day one of development. Our certified specialists guarantee the quality of the result. Learn more about 1C-Bitrix technology and configurators.
Contact us for a detailed audit of your project. We will assess the complexity for free and propose the optimal solution.







