Custom Components for Shopify App Blocks
Introduction
Use a Custom component when the Product Card Builder needs behavior or markup that is not covered by the built-in components.
To render one, drag the Custom component from the right sidebar into a Product Card block:
The Component Name must match a registered HTML Web Component:
customElements.define('rcc-custom-component', ProductCustomComponent);
If the browser has not registered that element when the product card renders, the Custom component renders nothing. Load the custom component script before the Nimstrata App Block renders.
Product Data Contract
The renderer creates the Web Component element and assigns product data as a JavaScript property:
element.product = {
...product,
metafields: configuredMetafields,
};
The product value is not serialized into an HTML attribute, so observedAttributes() is not needed for normal Product Card Builder custom components.
Configured product metafields are passed as product.metafields in the order configured in the builder. A metafield entry can be missing when Shopify has no value for the product, so guard optional fields.
Search, collection, and recommendation cards can provide slightly different product shapes. Custom components should treat optional fields as optional, especially variant, image, rating, and metafield data.
Custom Component Example
(function () {
'use strict';
class ProductCustomComponent extends HTMLElement {
set product(value) {
this._product = value;
this.render();
}
get product() {
return this._product;
}
connectedCallback() {
this.render();
}
render() {
if (!this._product) return;
const title = this._product.title || this._product.name || 'Product';
this.innerHTML = `
<div class="product-custom-component">
${title}
</div>
`;
}
}
customElements.define('rcc-custom-component', ProductCustomComponent);
})();
When adding custom component JavaScript files to the theme, include them in theme.liquid before the App Block renders:
{%- if request.page_type == 'search' or request.page_type == 'collection' %}
{{ 'rcc-custom-component.js' | asset_url | script_tag }}
{%- endif %}
Product Metafields
If a card needs Shopify Product metafields, add each namespace and key in the Product Card Builder. These values then become available in this.product.metafields or this._product.metafields, depending on the component implementation.
The builder does not fetch Shopify Variant metafields. If a card needs variant metafields, fetch them from Shopify's Storefront API inside the custom component.
Fetching Data from Shopify's Storefront API
There are times when the data from product is not complete enough for modals, quick views, or variant-specific metadata. In those cases, fetch additional data from Shopify's Storefront API.
The Storefront API token is available on Nimstrata App Block containers. Search and collection pages use .rcc-search; recommendation blocks use .rcc-recommendations.
async getVariantRRP() {
const variantId = this.product?.variantId || this.product?.variant?.id;
if (!variantId) return null;
const token =
document.querySelector('.rcc-search')?.dataset.token ||
document.querySelector('.rcc-recommendations')?.dataset.token;
if (!token) {
console.warn('Storefront API token not found');
return null;
}
const numericVariantId = String(variantId).replace(/^gid:\/\/shopify\/ProductVariant\//, '');
const variantGid = `gid://shopify/ProductVariant/${numericVariantId}`;
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;
}
}
Checklist
- Add a Custom item in the Product Card Builder layout
- Register the Web Component before the Nimstrata App Block renders
- Keep the component name stable after publishing
- Guard optional fields for search, collection, and recommendation placements
- Fetch variant metafields with Shopify Storefront API when required