1. Visually Help Center
  2. Visually for Developers

Extending Recommendation/Upsells with Custom Code

Although Visually provides many customizations to our Recommendations/Upsells widgets, we allow using custom hooks to extend their functionality.
In this tutorial you'll learn which custom hook we provide and examples on how to use them.

Modifying Product tiles/blocks

The onRecsProductEffect will be called every time the tiles/blocks are being re-rendered. This hooks is useful to modify/append elements inside each one of the product tiles/blocks.

window.loomi_api.onRecsProductEffect = function (element, ctx, product) {
console.log("onRecsProductEffect", element, ctx, product)
}


Where:

  • element - is the HTMLElement that represents the tile/block.

  • ctx - contains the widget's context, defined by the following interface:

    interface WidgetContext {
    experienceId?: string
    variantId?: string
    sectionId?: string
    widgetId?: string
    widgetVersion?: string
    atcEnabled?: boolean
    publishedAt?: number
    }
  • product - contains the product details defined by the following interface:

    interface Product {
    id: number;
    handle: string;
    image: Image;
    images?: Array<Image>;
    title: string;
    variants: Variant[];
    tags?: string[];

    quickAddToCartOptions?: string[];
    mainValues?: string[];
    mainOption?: string;

    reviewsCount?: number;
    reviewsRating?: number;
    }

    interface Variant {
    Size?: string;
    inventory_quantity?: number;
    image?: Image;
    price?: number;
    compare_at_price?: number;
    sku?: string;
    title?: string;
    variant_id?: any;
    Title?: string;
    Color?: string;
    { ... any other custom attribute ... }
    }

Example: Adding badges based on the product's discount

const badgeTemplate = (text) => `<div class="vsly_badge">${text}</div>`;

window.loomi_api.onRecsProductEffect = function (widget, ctx, product) {
const exists = widget.querySelector("vsly_badge");
if (!exists) {
const variant = product.variants[0]
const discount = Math.floot(1 - (variant.price / variant.compare_at_price) * 100)
if (discount > 10) {
widget.querySelector(".vsly-rec-title")
.insertAdjacentHTML("afterend", badgeTemplate(`${discount}% OFF`)
}
}
};

 

Modifying the Variant Selector Modal

The onRecsVariantSelectorEffect will be called when the modal is re-rendered or whenever the user selects a new variant.

window.loomi_api.onRecsVariantSelectorEffect = (
element,
ctx,
product,
variant
) => {
console.log("onRecsVariantSelectorEffect", element, ctx, product, variant)
};

where:

  • element - is the HTMLElement that represents the tile/block.

  • ctx - contains the widget's context.

  • product - contains the selected product's info.

  • variant - contains the selected variant's info.

element - is the HTMLElement that represents the tile/block.

ctx - contains the widget's context.

product - contains the selected product's info.

variant - contains the selected variant's info.

Running Code Before/After Adding Product to Cart

The onRecsBeforeATC and onRecsAfterATC is being called before/after adding an item to the cart.

window.loomi_api.onRecsAfterATC = (opts, openCart, ctx, discounts) => {
console.log("onRecsAfterATC", opts, openCart, ctx, discounts);
}

window.loomi_api.onRecsBeforeATC = (opts, openCart, ctx, discounts) => {
console.log("onRecsBeforeATC", opts, openCart, ctx, discounts);
}


where:

  • opts - an object with various fields, defined by the following interface:

    interface AddToCartTracking {
    experienceVariantId: string
    productId: string
    productVariantId: string
    product?: Product
    variant?: Variant
    }
  • opts - an object with various fields, defined by the following interface:
  • openCart - a boolean indicates if the cart should be open after the item is being added.

  • ctx - contains the widget context.

  • discounts - contains information about Visually applied discounts, defined by the following interface:

    interface DiscountProps {
    isDisabled?: boolean;
    discount?: {
    message?: TextFieldContentProps;
    value?: SingleValueProps;
    type?: SingleValueProps;
    maxQuantity?: SingleValueProps;
    },
    }