Skip to content

ProductRecommendations Web Component

assets/product-recommendations.js exports the ProductRecommendations class, which extends HTMLElement and is registered as the custom element <product-recommendations>. This component lazily loads product recommendations when the element enters the viewport, improving initial page load performance.

Source: assets/product-recommendations.js

Overview

The ProductRecommendations component:

  • Uses Intersection Observer API to detect when the element enters the viewport
  • Fetches product recommendations from Shopify's recommendation API
  • Loads recommendations only when needed (lazy loading)
  • Uses a 200px bottom margin to start loading before the element is fully visible
  • Replaces the element's content with fetched recommendations

Class Structure

javascript
export class ProductRecommendations extends HTMLElement {
  constructor()
  connectedCallback()
  handleIntersection(entries, observer)
}

API Reference

MethodDescription
constructor()Initializes the component and binds the intersection handler
connectedCallback()Lifecycle hook that creates IntersectionObserver and begins observing
handleIntersection(entries, observer)Handles intersection events, fetches recommendations, and updates DOM

Method Details

constructor()

javascript
export class ProductRecommendations extends HTMLElement {
  constructor() {
    super();
    this.handleIntersection = this.handleIntersection.bind(this);
    this.observer = null;
  }
}

Initialization:

  • Binds the intersection handler method for proper context
  • Initializes observer reference as null

connectedCallback()

javascript
  connectedCallback() {
    this.observer = new IntersectionObserver(this.handleIntersection, {
      rootMargin: '0px 0px 200px 0px'
    });

    this.observer.observe(this);
  }

Behavior:

  • Creates IntersectionObserver with 200px bottom margin (starts loading 200px before element is visible)
  • Begins observing the element itself

handleIntersection(entries, observer)

javascript
  handleIntersection(entries, observer) {
    if (!entries[0].isIntersecting) return;

    observer.unobserve(this);

    const url = this.dataset.url;

    fetch(url)
      .then(response => response.text())
      .then(text => {
        const html = document.createElement('div');
        html.innerHTML = text;
        const recommendations = html.querySelector('product-recommendations');

        if (recommendations && recommendations.innerHTML.trim().length) {
          this.innerHTML = recommendations.innerHTML;
        }
      })
      .catch(e => {
        console.error(e);
      });
  }

Behavior:

  1. Checks if element is intersecting (visible)
  2. Stops observing after first intersection (loads only once)
  3. Fetches recommendations from URL in data-url attribute
  4. Parses response HTML and extracts the recommendations content
  5. Replaces element's innerHTML with fetched recommendations (only if content exists)
  6. Handles errors by logging to console

Note: The component only updates if the fetched content has non-empty innerHTML, preventing empty states from being displayed.

Custom Element Definition

javascript
if (!customElements.get('product-recommendations')) {
  customElements.define('product-recommendations', ProductRecommendations);
}

Ensures the element is registered only once across bundles or hot reload sessions.

Integration with Shopify Liquid

liquid
<product-recommendations
  data-url="{{ routes.product_recommendations_url }}?product_id={{ product.id }}&limit=4"
>
  <!-- Placeholder content or loading state -->
  <div class="product-recommendations__loading">
    Loading recommendations...
  </div>
</product-recommendations>

<script src="{{ 'product-recommendations.js' | asset_url }}" type="module"></script>

Implementation Notes

  • The component requires a data-url attribute with the recommendations API endpoint
  • Recommendations are loaded lazily when the element approaches the viewport (200px margin)
  • The component only loads once per page load (stops observing after first intersection)
  • If the fetched content is empty, the element's content remains unchanged
  • The component gracefully handles fetch errors without breaking the page
  • Use Shopify's routes.product_recommendations_url helper to generate the correct URL
  • The component works with Shopify's built-in product recommendations feature