# Custom Components for Shopify App Blocks

## Introduction

If you have specific requirements for your product card or product listing pages, or you just want to create a feature that we haven't thought of yet, you can create a **Custom Component** in the Product Card Builder.

To begin, simply drag the **Custom** component from the right sidebar into a Product Card block:

![Product Card Builder with a Custom Component](/static/images/product-card-builder-custom-component.png)

The **Component Name** is the crucial setting to tell the product card which [HTML Web Component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) to render and should match your component's defintion, such as:

```js
customElements.define('rcc-custom-component', ProductCustomComponent);
```

<br/>

## Custom Component Example

Each component will automatically return Shopify product data including as price, product tags, variants, and more. The code example below will log the product data to your console from the `this._product` object - this is excellent for troubleshooting or seeing the full capabilities of Shopify:

```js assets/rcc-custom-component.js
(function () {
  'use strict';

  class ProductCustomComponent extends HTMLElement {
    constructor() {
      super();
    }

    static get observedAttributes() {
      return ['product'];
    }

    get product() {
      return this._product;
    }

    set product(value) {
      this._product = value;
      this.render();
    }

    connectedCallback() {
      if (this._product) {
        this.render();
      }
    }

    render() {
      console.log(this._product);

      this.innerHTML = `
          <div class="product-custom-component">
            Example Product 
          </div>
          `;
    }
  }

  customElements.define('rcc-custom-component', ProductCustomComponent);
})();
```

When adding custom component JS files to your theme, make sure to include them in your `theme.liquid` file before the App Block renders:

:::code language="js" title="theme.liquid" source="/static/code-snippets/liquid-include.liquid" :::

### Fetching Custom Data and Metafields

If you need to fetch Metafields that belong to the **Product** object in Shopify, we provide a simple way of fetching these values on render. Simply provide one or more **Keys** and associated **Namespaces** in the Product Card Builder.

These will then become available in `this._product.metafields`

Because Web Components are JavaScript, you also have full access to the `fetch` function in case you need to fetch any additional data, or even create POST requests for custom add to cart functions etc.

### Fetching Data from Shopify's Storefront API

There are times when the data from `this._product` is not complete enough for rendering modals or quick view containers. In this case you may need to fetch data from the Shopify Storefront API. The official Shopify documentation contains all of the potential data available to you:

[!ref target="\_blank" text="Shopify GraphQL Storefront API"](https://shopify.dev/docs/api/storefront/latest)
[!ref target="\_blank" text="Shopify Product Object"](https://shopify.dev/docs/api/storefront/latest/objects/Product)

Below is an example of a function that fetches data from the Shopify's Storefront API. Note this is an `async` function, so it requires an `await` e.g. `await this.getVariantRRP()` and if used inside the `render` function in the example above, will also need to be merged.

```js rcc-custom-component.js
async getVariantRRP() {
    if (!this._product?.variantId) {
      return null;
    }

    const token = document.querySelector(".rcc-search")?.dataset.token;
    if (!token) {
      console.warn("Storefront API token not found");
      return null;
    }

    const variantGid = `gid://shopify/ProductVariant/${this._product.variantId}`;

    const query = `
      query getVariantMetafield($id: ID!) {
        node(id: $id) {
          ... on ProductVariant {
            metafield(namespace: "custom", key: "variant_rrp") {
              value
            }
          }
        }
      }
    `;

    try {
      const response = await fetch("/api/2025-07/graphql.json", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Shopify-Storefront-Access-Token": token,
        },
        body: JSON.stringify({
          query,
          variables: { id: variantGid },
        }),
      });

      const data = await response.json();
      return data?.data?.node?.metafield?.value || null;
    } catch (error) {
      console.error("Error Fetching Variant RRP:", error);
      return null;
    }
  }
}
```
