← Back to blog

Magento 2 Configurable Products Performance: What to Fix

Rodrigo Biazzi

Configurable products are where catalog design decisions turn into performance problems. A parent with twenty children is cheap. A parent with eight hundred children, rendered with swatches on a category page, is not. This article covers where the cost goes in Magento Open Source and Adobe Commerce 2.4.8 and what I change first when configurables are the reason pages are slow.

How a configurable product is stored

A configurable product is a parent with no price or stock of its own. Its children are simple products, usually Not Visible Individually. The relationship lives in catalog_product_super_link, the configurable attributes in catalog_product_super_attribute, and the generic parent and child relation in catalog_product_relation, which indexers use.

Everything the storefront shows for the parent is derived from the children. The price is the lowest salable child price. The stock status depends on whether any child is salable. The swatches and the option list come from the children's attribute values. So every place that renders a configurable has to look at its children, and the cost grows with the number of children.

Where the cost goes

Product page

On the product page, Magento\ConfigurableProduct\Block\Product\View\Type\Configurable builds a JSON config with every child: option IDs, prices for each child, images and stock. With swatches the block from Magento_Swatches adds swatch data on top. For a parent with hundreds of children, that JSON can reach hundreds of kilobytes in the HTML. The server time goes into loading children with their prices and images, and the browser then has to parse the JSON and run the swatch widget on it.

Magento caches the list of used products through Magento\ConfigurableProduct\Model\Plugin\Frontend\UsedProductsCache, which helps on repeated loads. It does not shrink the JSON.

Category pages with swatches

When Show Swatches in Product List is enabled, each configurable in the listing gets its own swatch renderer and its own JSON config. Twenty four configurables with many children each can make the listing page much heavier than the product page. The settings are under Stores, Configuration, Catalog, Catalog, Storefront:

bin/magento config:show catalog/frontend/show_swatches_in_product_list
bin/magento config:show catalog/frontend/swatches_per_product

Swatches per Product limits how many swatches are rendered per item. It reduces the HTML, but the child data still has to be loaded. If the listing does not need swatches, turning them off is the biggest single gain.

Admin product save

Saving a parent in the admin saves the relations, and with Update on Save indexers it reindexes the parent and its children. The product form also loads the configurations grid with every child. Large parents make the edit page slow to open and slow to save. Putting the indexers on schedule moves the indexing cost out of the request, see indexer modes: save vs schedule.

Indexers

The price indexer computes configurable prices from the children for every website and customer group. The stock indexer computes parent stock from children. The search indexer adds child attribute values to the parent document, so that searching for a child's color finds the parent. All three grow with the number of children times websites and customer groups.

Fix the data model first

Most slow configurables I look at have a modeling problem, not a code problem.

  • Too many configurable attributes. Color times size times length times material produces hundreds of combinations, many of which are never stocked. If an option only changes something small, consider a custom option, or split the product.
  • Children that never sell. Children created for every combination "just in case" still get indexed and rendered. Delete combinations that do not exist in the real world.
  • Color as a separate product. If each color has its own photos, its own description and should rank on its own in search engines, it is often better as its own configurable for size, linked to the others. This also cuts the child count per parent.
  • Attributes only on the parent. Values that are the same for all children, such as brand or material, belong on the parent and do not need to be repeated as varying data.

There is no hard limit on children, but in my experience parents with a few dozen children behave well, and parents with several hundred need attention on every page that renders them.

Configuration changes

Price indexer dimensions

In Magento Open Source the price index can be split into dimension tables by website and customer group. This keeps each table smaller and makes partial reindexing cheaper on stores with several websites or many customer groups.

bin/magento indexer:show-dimensions-mode catalog_product_price
bin/magento indexer:set-dimensions-mode catalog_product_price website_and_customer_group
bin/magento indexer:reindex catalog_product_price

Run it on staging first and time the reindex before and after. On a single website with the default groups the gain is small.

Out of stock children

With Display Out of Stock Products set to No, out of stock children are left out of the JSON config and the option list, which makes it smaller. Whether that is acceptable is a business decision, because customers no longer see sizes that will come back in stock.

Product image settings

Each child image adds entries to the gallery data for the parent. If children share the parent's images, do not upload copies on every child. Uploading the same photo to fifty children multiplies storage, resize work and JSON size. Image handling is covered in product images optimization.

Measure the real cost

Before changing anything, find out which part is slow. A quick way to see the size of the JSON on a product page:

curl -s https://example.test/some-configurable.html \
  | grep -o '"jsonConfig":.*' | wc -c

For server time, profile an uncached product page and a category page with a profiler such as Blackfire, Tideways or New Relic, and look at the time spent under the configurable and swatches renderer blocks and the number of SQL queries. In the database, this query lists the parents with the most children:

SELECT p.sku, COUNT(l.product_id) AS children
FROM catalog_product_super_link l
JOIN catalog_product_entity p ON p.entity_id = l.parent_id
GROUP BY l.parent_id, p.sku
ORDER BY children DESC
LIMIT 20;

On Adobe Commerce, the join key on catalog_product_entity is row_id instead of entity_id because of content staging, so adjust the query there.

Code level fixes

When the data model is fine and the store still needs large parents, there are code changes that help.

  • Load swatches lazily on listings. Render a simple color list in the listing and load the full swatch config only when the customer interacts. This needs a custom renderer and is theme specific, but it removes most of the listing cost.
  • Check third party plugins. Modules that hook into getUsedProducts() or the JSON config, for example to add extra child data, often load full child models in a loop. Profile with and without them.
  • Avoid loading children in templates. Custom listing templates that call getTypeInstance()->getUsedProducts() per product create an N+1 problem. Get the data from the indexed collection instead.
  • Cache aggressively. Product and category pages should be served from full page cache. Most of the cost above is paid on cache misses, so a high hit rate hides a lot. Varnish setup is in Varnish full page cache.

Hyva and headless storefronts

A different frontend does not remove the backend cost of loading children, but it changes the browser side. Hyva replaces the Knockout and jQuery swatch widget with lighter Alpine.js components. A headless storefront fetches variants through GraphQL, where you control which fields are requested. In both cases the data model advice above still applies.

FAQ

How many children can a Magento configurable product have?

There is no fixed limit in the code. Performance degrades as the count grows. A few dozen children work well. Several hundred need profiling and usually a change in modeling, swatch settings or rendering.

Why is the configurable product price wrong after a change?

The parent price comes from the price index, computed from salable children. If indexers are on schedule and cron is not running, or a child became out of stock without a reindex, the parent shows the old value.

Do swatches slow down category pages?

Yes, when Show Swatches in Product List is enabled, because each configurable in the listing loads child data and renders its own JSON config. Disabling them or limiting swatches per product reduces the cost.

Should I use configurable or simple products with custom options?

Use configurable when each variation has its own SKU, stock or price. Use custom options for personalization that has no stock of its own. Mixing them up is a common cause of bloated catalogs.

I am available for freelance Magento and Adobe Commerce work, including catalog performance reviews. If you need help with this, get in touch.

Related articles