Product List Item Display Card
Replaces the app's default product card. Rendered once per product, wherever a product list is shown - collection grids, search results, related/upsell lists, etc. Each render gets exactly one product via data.product; the block isn't responsible for fetching or iterating a product list itself.
type: "product-display-card" - a block without settings - just one .tsx file, no settings panel.
Data
data is { product: ShopifyProduct }, already resolved (no fetching needed).
Common fields/actions:
data.product.title- product titledata.product.images[0].url- primary image URLdata.product.variants[0].price- first variant's price (render with<Money price={...} />from@evlop/shopify)data.product.actions.openDetailsPage- pass to<Actionable action={...}>to make the whole card tap through to the product pagedata.product.actions.addToCart- pass to a<Button action={...}>for an add-to-cart control
Loading state
data.product can also be { isLoading: true } while the real product is still loading. Always check data.product?.isLoading before reading title/images/variants, and render a skeleton/placeholder matching the card's own layout dimensions (image area, title line, price line) rather than blank or broken output.
Loading metafields
To show metafield data (e.g. a material or fit detail), register which metafields to load with loadProductsManager.addMetafieldIdentifiers([{ namespace, key }, ...]) from @evlop/shopify. Call this once at module scope, alongside the imports - never inside the component body. A registered { namespace: 'custom', key: 'vendor' } identifier lands at data.product.metafields.custom.vendor - see Showing Metafields on a Product Display Card for a full worked example.
Example
import React from 'react';
import { Actionable, Flexbox, Image, Text, Button } from '@evlop/native-components';
import { Money, CustomBlock } from '@evlop/shopify';
const ProductDisplayCard: CustomBlock.ProductListItem = ({ data }) => {
return (
<Actionable action={data.product.actions.openDetailsPage}>
<Flexbox flexDirection="column" gap={10}>
<Flexbox flexDirection="column" gap={5}>
<Image
borderRadius={5}
resizeMode="cover"
src={data.product.images[0].url}
aspectRatio={1}
/>
</Flexbox>
<Flexbox flexDirection="column" gap={4}>
<Flexbox alignItems="center" justifyContent="space-between">
<Text fontWeight="Bold" color="primary">
<Money price={data.product.variants[0].price} />
</Text>
<Button
py={8}
variant="ghost"
color="primary"
icon="material-community-icons:cart-plus"
size="sm"
action={data.product.actions.addToCart}
/>
</Flexbox>
<Text numberOfLines={2} fontSize="sm">
{data.product.title}
</Text>
</Flexbox>
</Flexbox>
</Actionable>
);
};
export default ProductDisplayCard;