> ## Documentation Index
> Fetch the complete documentation index at: https://docs.depict.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Setting up your product card

> Your product card components needs to meet some specific requirements

<Warning>
  This page does not apply to installs made in 2025 or later of the Depict Shopify apps
</Warning>

## Requirements

For both search and product listing pages, you need to set up your product card component.

That component will receive a `display` of the type `YourDisplay` (see [previous page](/react-ui-guide/setup/depict-provider)) <b>OR `null`</b>.

If `display` is `null`, it means that there's no data for this product card yet, and you should display <b>a product card as close to the dimensions as the one you'll display when you'll have data</b>, we'll call this a "placeholder". The placeholder serves as a loading indicator for users as well as enabling scroll restoration when going back and minimizing content shift.

## Example expected output of your product card component

<CardGroup cols={2}>
  <Frame caption="`display` is `null`">
    <img
      src="https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=8b96b949b362cefa656637ab62081257"
      style={{ aspectRatio: 457/903, width: "100%"}}
      ref={element => {
    // In prod, they have some zoom thing which breaks aspectRatio, work around that. Unfortunately this is not in the SSR'd output so there's still some CLS.
    const direct_parent = element?.parentElement;
    if(direct_parent?.matches?.("[data-rmiz-content]")) {
        Object.assign(direct_parent.style, {
            width: "100%",
            display: "flex",
            justifyContent: "center",
            alignItems: "center"
        })
    }
    const parents_parent = direct_parent?.parentElement;
    if(parents_parent?.matches?.("[aria-owns^='rmiz-modal']")) {
        parents_parent.style.width = "100%";
    }
    }}
      data-og-width="457"
      width="457"
      data-og-height="903"
      height="903"
      data-path="images/placeholder.svg"
      data-optimize="true"
      data-opv="3"
      srcset="https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=280&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=c40148726d1f8a68353bde9b1b34d718 280w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=560&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=635f7791e3f386286cfd1549c6a7238b 560w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=840&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=6a20d33dd641f3a10a201142f845e987 840w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=1100&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=5ce3275c0191bf0d66dcae6049184742 1100w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=1650&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=f7a0e748b73764ad382c44346d7c3f37 1650w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/placeholder.svg?w=2500&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=e297af892bfdd0a8fd48126cfbbbbb18 2500w"
    />
  </Frame>

  <Frame caption="`display` is `HoudiniDisplay`">
    <img
      src="https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=a3c900bb57e3726b1db2f38f68562d87"
      style={{ aspectRatio: 469/928, width: "100%"}}
      ref={element => {
    // In prod, they have some zoom thing which breaks aspectRatio, work around that. Unfortunately this is not in the SSR'd output so there's still some CLS.
    const direct_parent = element?.parentElement;
    if(direct_parent?.matches?.("[data-rmiz-content]")) {
        Object.assign(direct_parent.style, {
            width: "100%",
            display: "flex",
            justifyContent: "center",
            alignItems: "center"
        })
    }
    const parents_parent = direct_parent?.parentElement;
    if(parents_parent?.matches?.("[aria-owns^='rmiz-modal']")) {
        parents_parent.style.width = "100%";
    }
    }}
      data-og-width="469"
      width="469"
      data-og-height="928"
      height="928"
      data-path="images/product_card.svg"
      data-optimize="true"
      data-opv="3"
      srcset="https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=280&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=79e2de8ed041a8405134a66bef73c691 280w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=560&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=a876db34a2be5b46bbcf0c742c18422c 560w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=840&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=048a8faa4cd4fc9a35f4b80361e7d707 840w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=1100&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=0eb7c50d4873240d5ec1ff725e79e9dc 1100w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=1650&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=73eaca34dffbebe680b83bffed047604 1650w, https://mintcdn.com/depictai/oqYy6W_ukzaDVZL8/images/product_card.svg?w=2500&fit=max&auto=format&n=oqYy6W_ukzaDVZL8&q=85&s=88a578d23f1b01027093c4fb1fbd3d56 2500w"
    />
  </Frame>
</CardGroup>

## Provided Placeholder components

To make it easier for you to create a placeholder, we provide you with a few components that you can use to create a placeholder.

You can understand their usage from the example below or [read the reference](/reference/listings-search/placeholder-components).

## Displaying color swatches

```ts theme={null}
export interface YourDisplay {
  /**
  * The index of the variant to show in the variant_displays array
  */
  variant_index: number;
  /**
  * All variants for that product - here you can, for example, get the color of each variant to display color swatches
  */
  variant_displays: YourVariantDisplay[];
  // … other properties
}
```

## Code example

You probably have an existing product card which you just can add placeholder functionality for. Here's an example of how it could look like:

```tsx theme={null}
import { ImagePlaceholder, TextPlaceholder } from "@depict-ai/react-ui";

export function ProductCard({ display }: { display: null | YourDisplay }) {
  const variant_to_show = display?.variant_displays[display?.variant_index];

  return [
    <Link
      href={variant_to_show?.page_url || "#"}
      className="rec-outer"
      {...(variant_to_show && "recommendation_id" in variant_to_show
        ? { "data-recommendation-id": variant_to_show.recommendation_id }
        : {})}
    >
      {variant_to_show ? (
        <div className="image-container">
          {/* Use the first image in image_urls as the default image */}
          <img className="default-image" src={variant_to_show.image_urls[0]} />
          {/* Use the second image in image_urls as the hover image */}
          <img className="hover-image" src={variant_to_show.image_urls[1]} />
        </div>
      ) : (
        <ImagePlaceholder aspectRatio={imgAspectRatio} />
      )}

      <div className="text-container">
        <div className="rec-title">
          {variant_to_show.title ?? <TextPlaceholder height="1em" width="20ch" />}
        </div>
        <div className="price-container">
          <span className="price">
            {variant_to_show ? (
              <FormatPrice price={variant_to_show.sale_price} />
            ) : (
              <TextPlaceholder height="1em" width="5ch" />
            )}
          </span>
        </div>
      </div>
    </Link>,
  ];
}
```

<Note>Note how we branch off to the shimmering placeholder effect in place of the content, when there is no content</Note>
