# Defaults: ProductCard to ProductGrid (/themes/sdk/reference/listing/defaults-2)

```ts
import { … } from '@poltura/theme-sdk/listing';
```

## `ProductCard` [#productcard]

A complete product card: image, discount, title, badges, price and an add-to-cart button that opens the product page when options must be chosen.

```ts
ProductCard(props: ProductCardProps): ReactElement
```

The button adds the card's variant, or its fixed bundle, to the cart and shows the store's error under it when that fails. Prices follow the visitor's price access.

**Props:** [`ProductCardProps`](/themes/sdk/reference/listing/types-2#productcardprops)

**Example**

```tsx
import { ProductCard, useListing } from '@poltura/theme-sdk/listing';

export function FirstProduct() {
  const [first] = useListing().products;
  return first ? <ProductCard card={first} show={{ addToCart: false }} /> : null;
}
```

**See also:** [`ProductGrid`](/themes/sdk/reference/listing/defaults-2#productgrid), [`ProductCardBadge`](/themes/sdk/reference/listing/parts-2#productcardbadge), [`ProductCardSpecs`](/themes/sdk/reference/listing/parts-2#productcardspecs)

## `ProductGrid` [#productgrid]

A responsive grid of product cards, with placeholders while loading and [`ListingEmpty`](/themes/sdk/reference/listing/parts-2#listingempty) when there are none.

```ts
ProductGrid(props: ProductGridProps): ReactElement
```

**Props:** [`ProductGridProps`](/themes/sdk/reference/listing/types-3#productgridprops)

<Tab value='Example'>

```tsx
import { ProductGrid, useListing } from '@poltura/theme-sdk/listing';

export function ListingGrid() {
  const { products, layout, isPending } = useListing();
  return <ProductGrid cards={products} columns={layout.columns} imageAspectRatio={layout.imageAspectRatio} loading={isPending} />;
}
```

</Tab>
<Tab value='Source'>

```tsx title="source/product-grid.tsx"
import type { ReactElement } from 'react';
import {
  ListingEmpty,
  ProductCard,
  ProductList,
  type ProductGridProps,
} from '@poltura/theme-sdk/listing';
import { cn } from '@poltura/theme-sdk/ui';

const PLACEHOLDER_CARDS = 20;

function desktopColumns(count: number): string {
  if (count <= 2) return 'md:grid-cols-2';
  if (count === 3) return 'md:grid-cols-3 xl:grid-cols-3';
  if (count >= 5) return 'md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5';
  return 'md:grid-cols-3 xl:grid-cols-4';
}

function mobileColumns(count: number): string {
  return count <= 1 ? 'grid-cols-1' : 'grid-cols-2';
}

export function ProductGrid({
  cards,
  layout = 'grid',
  columns,
  show,
  imageAspectRatio,
  labels,
  empty,
  loading = false,
  className,
  listName,
}: ProductGridProps): ReactElement {
  const gridCols =
    layout === 'list'
      ? 'grid-cols-1'
      : columns
        ? cn(mobileColumns(columns.mobile), desktopColumns(columns.desktop))
        : 'grid-cols-2 md:grid-cols-3 xl:grid-cols-4';

  if (loading) {
    return (
      <div className={cn('grid gap-3 md:gap-6', gridCols, className)}>
        {Array.from({ length: PLACEHOLDER_CARDS }).map((_, index) => (
          <div key={index} className='animate-pulse'>
            <div className='bg-muted rounded-lg h-48 sm:h-64 mb-4'></div>
            <div className='space-y-2 px-2'>
              <div className='h-4 bg-muted rounded-sm w-3/4'></div>
              <div className='h-4 bg-muted rounded-sm w-1/2'></div>
            </div>
          </div>
        ))}
      </div>
    );
  }

  if (cards.length === 0) return <ListingEmpty heading={empty?.heading} body={empty?.body} />;

  return (
    <ProductList cards={cards} name={listName}>
      <div className={cn('grid gap-3 md:gap-6', gridCols, className)}>
        {cards.map((card) => (
          <ProductCard
            key={card.id}
            card={card}
            layout={layout}
            show={show}
            imageAspectRatio={imageAspectRatio}
            labels={labels}
          />
        ))}
      </div>
    </ProductList>
  );
}
```

</Tab>


**See also:** [`ProductCard`](/themes/sdk/reference/listing/defaults-2#productcard), [`ProductList`](/themes/sdk/reference/listing/parts-2#productlist), [`useListing`](/themes/sdk/reference/listing/hooks#uselisting)
