Recommendations

FAQ

Frequently asked questions about Clerk.io Recommendations.

Hide Category slider #

When a Clerk.io Recommendation is added to a category page that contains a low amount of products, it will show the same products on the page.

To avoid this, you can hide the Recommendation on category pages, by adding an if statement to the Recommendation design code.

Change the number in the if statement to the minimum amount of products needed to show the Recommendation.

{% if products.length > 7 %}
 
 <!-- Recommendation code -->

{% endif %}

E.g.:

{% if products.length > 7 %}
<div class="clerk-wrapper">
    <div class="clerk-headline-wrapper">
        <h2 class="clerk-headline">{{headline}}</h2>
    </div>
    <div class="clerk-slider-wrapper">
        <div class="clerk-slider">
            {% for item in products %}
            <div class="clerk-slider-content">
                <!-- START PRODUCT CARD -->
                <!-- END PRODUCT CARD -->
            </div>
            {% endfor %}
        </div>
    </div>
</div>
{% endif %}

This approach only hides the slider, but still counts towards your Recommendation usage. For a few categories, this method works well.

However, if you have many categories with low amount of products, we recommend blocking the slider injection at the server level instead.

Recommend compatible products #

Clerk’s AI recommends products based on what is most likely to be bought together. This works well for most stores, but some catalogues contain products that must be physically or technically compatible — like car parts, electronics components, or machine spare parts.

In these cases, a product being popular is not enough. It also has to fit the product a customer is looking at.

The solution is to tag your products with attributes that define compatibility, and then use dynamic filtering to restrict recommendations to only matching products.

Clerk’s AI will still determine which products are the best to show, but only within the subset that passes the filter. This means you get the best of both worlds — intelligent ranking within a compatible selection.

Adding attributes #

The attributes you need depend on how compatibility works in your catalogue. They should be synced to Clerk as part of your product data, either through your integration or via the API.

You can verify that your attributes are available by going to Data > Products and browsing any product.

Here are three common patterns, ranging from simple to advanced.

Single attribute #

If all products from the same vendor are guaranteed to be compatible, add the vendor as an attribute on each product. Then filter recommendations to only show products with the same vendor.

In your embed code, define a variable containing the current product’s vendor and use it as a filter:

<span class="clerk"
      data-template="@product-page-alternatives"
      data-products='["CURRENT_PRODUCT_ID"]'
      data-filter="vendor = $product_vendor">
</span>
var product_vendor = "ACME Corp";

This ensures that a customer viewing a product from ACME Corp will only see recommendations from the same vendor.

Multiple attributes #

Sometimes a single attribute is not enough. For example, a car part might need to match both the make and model of a vehicle.

You can combine multiple conditions in a single filter:

<span class="clerk"
      data-template="@product-page-alternatives"
      data-products='["CURRENT_PRODUCT_ID"]'
      data-filter="make = $product_make and model = $product_model">
</span>
var product_make = "Toyota";
var product_model = "Corolla";

Only products matching both the make and model will be shown.

Compatibility groups #

In more complex catalogues, each product has a unique list of other products it is compatible with, and this cannot be captured by shared attributes alone.

The solution is to compute a group identifier based on the set of compatible product IDs, and store it as an attribute. Products that share the exact same set of compatible items will get the same identifier, allowing you to filter by it.

For example, if products A, B and C are all compatible with each other, you could compute a hash from their sorted IDs and store it as a compatibility_group attribute on each of them. Then filter by it:

<span class="clerk"
      data-template="@product-page-alternatives"
      data-products='["CURRENT_PRODUCT_ID"]'
      data-filter='compatibility_group = $product_group'>
</span>
var product_group = "a1b2c3d4";

This approach works well when compatibility relationships come from an external database or PIM system. The hash should be recomputed whenever the compatibility data changes, and synced to Clerk along with the rest of your product data.

Tip: If a product can belong to multiple compatibility groups, store the identifiers as a list attribute and use the in operator instead of =. Read more about filter syntax.

Choosing the right approach #

ScenarioAttributeExample filter
Same-brand products always fitvendorvendor = $product_vendor
Fits a specific vehicle or devicemake + modelmake = $product_make and model = $product_model
Predefined compatibility listscompatibility_groupcompatibility_group = $product_group

Start with the simplest approach that covers your use case. You can always add more attributes later if you need finer control.

For a deeper look at all the ways you can use filters in embed codes, see Dynamic Filtering.

Show Recommendations in a dynamic sidecart #

A sidecart is a cart drawer, minicart, basket overlay, or other cart interface that opens and updates without loading a new page. Its HTML can change when a visitor adds a product, removes a product, or changes a quantity.

Recommendations in this type of cart must follow the live cart state. A normal Injection that renders once on page load is not enough. The implementation needs to coordinate the sidecart, cart events, the Clerk.io Element, and the recommendation design.

Before you start #

Inspect the live sidecart before creating the Element or writing the design script. Record the sidecart container, content area, product ID selector and attribute, loading indicator, cart update events, and whether updates replace the sidecart body or the entire container.

Do not copy selectors, attributes, or events from another store. Sidecart markup varies between themes and plugins. Use the platform’s existing cart events whenever they are reliable; otherwise, use the smallest filtered MutationObserver needed to detect cart changes. Avoid observing the entire body without filtering, because Clerk.io’s rendered markup can trigger the observer again.

Create the Element #

Create a dedicated Element for the sidecart. Use the recommendations/complementary API and the Best Cross-Sell Products logic. Configure it to read product IDs from the live cart, exclude products already in the cart, and return around four to eight products.

The preferred configuration is to pass the cart IDs in the Element settings: set the API to recommendations/complementary, the product ID type to products, the ID source to the Element, and the ID selector and attribute to the values discovered in the sidecart.

If the sidecart does not contain its product IDs when Clerk.io initializes the Element, set the parameters from the design script instead:

contentElement.setAttribute("data-api", "recommendations/complementary");
contentElement.setAttribute("data-products", ids.join(","));
contentElement.setAttribute("data-exclude", ids.join(","));
contentElement.setAttribute("data-limit", "8");

Clerk("content", "#" + contentElement.id, function (content) {
  content.param({
    api: "recommendations/complementary",
    products: ids,
    exclude: ids,
    limit: 8
  });
});

Treat this script-based configuration as a fallback. It must run after the current cart IDs are available, not only when the page first loads.

Mount the Element #

Place the Element at a stable location and move the rendered recommendation root into the sidecart after Clerk.io renders it. Use independent clerk- classes so the design does not inherit old provider styles such as hr- or aw- classes.

On desktop, a recommendation rail can sit beside the sidecart:

.clerk-sidecart-recs {
  position: absolute;
  top: 0;
  right: 100%;
  width: 250px;
  height: 100vh;
}

If the platform clips the rail, add overflow: visible !important to the sidecart host class. On mobile, move the root into the sidecart body and use position: static.

Keep the Recommendations current #

Cart actions often fire several events in a short burst and may replace the sidecart body. Debounce cart events and relevant DOM mutations, then wait until the cart has finished updating before rendering.

Track a key made from the current product IDs, for example ids.join(","). Render only when the final key changes. If quantity changes should affect recommendations, include quantities in the key; otherwise, a quantity-only change does not need a new recommendation request.

Use a render lock and a short per-key cooldown to prevent overlapping or repeated renders while fragments settle. If the recommendation root is removed during a refresh, recover it once for the same cart key.

Track the cart key on both the Element and the visible recommendation root. Before applying an asynchronous response, compare the response key with the current cart key. This prevents an older response from replacing recommendations for a newer cart.

var key = ids.join(",");
contentElement.setAttribute("data-products", ids.join(","));
contentElement.setAttribute("data-exclude", ids.join(","));
contentElement.setAttribute("data-clerk-sidecart-request-key", key);
root.setAttribute("data-clerk-sidecart-render-key", key);

if (getCartProductIds().join(",") !== key) return;

Avoid using the presence of existing product cards as the only guard. An initial render can happen before cart IDs are available and otherwise freeze the sidecart on the wrong products.

Fallback rendering #

If the Clerk.io root disappears after a cart refresh, mark a manually created fallback root with data-clerk-sidecart-fallback="true". Hide it while empty or loading, remove it when a real Clerk.io root appears, and cache fallback HTML by cart key so recovery does not make duplicate API calls.

Desktop and mobile layout #

On desktop, the recommendation rail can sit beside the sidecart. On mobile, move it into the sidecart content area and show one product at a time. Use either Clerk.io’s native slider controls or custom controls, but do not display both sets of arrows.

If the design owns the controls, hide Clerk.io’s generated controls within this design:

.clerk-sidecart-recs .clerk-slider-nav,
.clerk-sidecart-recs .clerk-slider-prev,
.clerk-sidecart-recs .clerk-slider-next,
.clerk-sidecart-recs .clerk-slider-button,
.clerk-sidecart-recs [class*="clerk-slider-arrow"] {
  display: none !important;
}

Moving a rendered root or replacing cart fragments during slider initialization can leave product cards without visible arrows. Decide which code owns the controls and test that ownership after every refresh.

Add to cart #

If recommendation cards have add-to-cart buttons, use the platform’s AJAX endpoint and prevent normal link navigation. After success, trigger the platform’s standard cart event if needed and schedule one settled sync. When Clerk.js is available, also track the action with Clerk("cart", "add", productId, { quantity: 1 });.

Verify the result #

Verify an existing cart and an empty cart. Test opening and closing the sidecart, adding and removing products, changing quantities, and updating without a full page refresh.

Check desktop rail placement, mobile inline placement, one-product mobile slides, loading, empty, and error states. Confirm that no empty headline-only placeholder is visible while the cart is loading.

In the browser network panel, inspect calls to api.clerk.io. The same final cart key should not trigger duplicate recommendation requests, and an older response must not replace the current recommendation set. Finally, add a recommended product from inside the sidecart and confirm that the cart, tracking, and recommendations all update correctly.