---
name: product-card-featured
title: Product Card Featured
url: https://ui.frontic.com/components/sections/product-card-featured.md
tier: sections
install: npx @frontic/ui add product-card-featured
files:
  - components/sections/product-card-featured/ProductCardFeatured.vue
  - components/sections/product-card-featured/index.ts
  - components/sections/product-card-featured/types.ts
registryDependencies:
  - badge
  - button
  - card
  - image
  - price
  - product
  - swatch-group
  - use-product-sibling
dependencies:
  - "@lucide/vue"
---

# Product Card Featured

Large product card with a wishlist toggle and a feature badge on the photo, the brand above the title, a description, swatches that switch the image, and a prominent price beside a compare toggle.

## Install

```sh
npx @frontic/ui add product-card-featured
```

Built from: [Badge](https://ui.frontic.com/components/ui/badge.md), [Button](https://ui.frontic.com/components/ui/button.md), [Card](https://ui.frontic.com/components/ui/card.md), [Image](https://ui.frontic.com/components/ui/image.md), [Price](https://ui.frontic.com/components/ui/price.md), [Product](https://ui.frontic.com/lib/product.md), [Swatch Group](https://ui.frontic.com/components/ui/swatch-group.md), [Use Product Sibling](https://ui.frontic.com/composables/use-product-sibling.md)

npm packages: `@lucide/vue`

## Examples

```vue
<script setup lang="ts">
import { ProductCardFeatured, type Product } from '@/components/sections/product-card-featured'

const product: Product = {
  name: 'Frost Escape Parka',
  key: 'frost-escape-parka',
  brand: 'Northridge',
  description: 'Made for the city: keeps you warm, dry and stylish however hard winter hits.',
  link: { path: 'https://sw.demo-shop.com/frost-escape-parka-3abd4b58' },
  price: { amount: 14900, currency: 'EUR' },
  cover: { src: 'https://static.frontic.com/cdn-cgi/image/width=640,format=auto/ui/products/frost-escape-parka.jpg' },
  badge: 'Insulated',
}
</script>

<template>
  <div class="mx-auto w-full max-w-sm">
    <ProductCardFeatured :product="product" />
  </div>
</template>
```

## API reference

### ProductCardFeatured

Shows a product with its image and price, with compare and wishlist buttons.

Styling: `product-card-featured-description`, which extends [`hint`](https://ui.frontic.com/DESIGN.md#hint); [`title`](https://ui.frontic.com/DESIGN.md#title); [`meta`](https://ui.frontic.com/DESIGN.md#meta). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `product` (required) | `Product` |  | The product to show: brand, name, description, price, cover, badges and colour siblings. |
| `compareLabel` | `string` | `'Compare'` | Shown on the button that adds the product to the comparison. |
| `comparingLabel` | `string` | `'Comparing'` | Shown on the compare button while the product is being compared. |
| `wishlistLabel` | `string` | `'Add to wishlist'` | Screen-reader name of the heart button while the product is not on the wishlist. |
| `wishlistedLabel` | `string` | `'Remove from wishlist'` | Screen-reader name of the heart button while the product is on the wishlist. |
| `clearoutLabel` | `string` | `'Clearout'` | Shown on the badge of a product marked as clearout. |
| `inStoreLabel` | `string` | `'In Store'` | Shown on the badge of a product available in store. |
| `locale` | `string` |  | BCP 47 locale the price is formatted in, e.g. `de-DE`; the ConfigProvider's when left out. |
| `surface` | `'raised' \| 'bare'` | `'raised'` | Bare drops the edge, shadow and inset, for a section placed inside another Card. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `compare` | `product: Product` | Fires with the product when the shopper toggles it for comparison. |
| `wishlist` | `product: Product` | Fires with the product when the shopper toggles it on the wishlist. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-surface` | `'raised' \| 'bare'` | Carries the `surface` prop. |

## Exports

```ts
import { type Product, type ProductSibling } from '@/components/sections/product-card-featured'
```

- `Product`, `ProductSibling`: from [Product](https://ui.frontic.com/lib/product.md)

## Styling

| Part | Recipe |
| --- | --- |
| `image` | [`product-card-featured-image`](https://ui.frontic.com/DESIGN.md#product-card-featured-image) |
| `description` | [`product-card-featured-description`](https://ui.frontic.com/DESIGN.md#product-card-featured-description) |
| `title` | [`title`](https://ui.frontic.com/DESIGN.md#title) |
| `meta` | [`meta`](https://ui.frontic.com/DESIGN.md#meta) |

States with a look of their own: hover.

## Source

`components/sections/product-card-featured/ProductCardFeatured.vue`

```vue
<script lang="ts">
/** Shows a product with its image and price, with compare and wishlist buttons. */
export default {}
</script>

<script setup lang="ts">
import { HeartIcon, CheckIcon, ArrowRightLeftIcon } from '@lucide/vue'

import type { HTMLAttributes } from 'vue'
import type { Product } from './types'
import { ref } from 'vue'
import { useProductSibling } from '@library/composables/use-product-sibling'
import { Badge } from '@library/components/ui/badge'
import { Button } from '@library/components/ui/button'
import { Card, CardContent, CardImage } from '@library/components/ui/card'
import { Image } from '@library/components/ui/image'
import { Price } from '@library/components/ui/price'
import { SwatchGroup, SwatchGroupItem } from '@library/components/ui/swatch-group'

const props = withDefaults(
  defineProps<{
    /** The product to show: brand, name, description, price, cover, badges and colour siblings. */
    product: Product
    /** Shown on the button that adds the product to the comparison. */
    compareLabel?: string
    /** Shown on the compare button while the product is being compared. */
    comparingLabel?: string
    /** Screen-reader name of the heart button while the product is not on the wishlist. */
    wishlistLabel?: string
    /** Screen-reader name of the heart button while the product is on the wishlist. */
    wishlistedLabel?: string
    /** Shown on the badge of a product marked as clearout. */
    clearoutLabel?: string
    /** Shown on the badge of a product available in store. */
    inStoreLabel?: string
    /** BCP 47 locale the price is formatted in, e.g. `de-DE`; the ConfigProvider's when left out. */
    locale?: string
    /** Bare drops the edge, shadow and inset, for a section placed inside another Card. */
    surface?: 'raised' | 'bare'
    class?: HTMLAttributes['class']
  }>(),
  {
    surface: 'raised',
    compareLabel: 'Compare',
    comparingLabel: 'Comparing',
    wishlistLabel: 'Add to wishlist',
    wishlistedLabel: 'Remove from wishlist',
    clearoutLabel: 'Clearout',
    inStoreLabel: 'In Store',
  },
)
const emit = defineEmits<{
  /** Fires with the product when the shopper toggles it for comparison. */
  compare: [product: Product]
  /** Fires with the product when the shopper toggles it on the wishlist. */
  wishlist: [product: Product]
}>()

const { selected: selectedSibling, cover } = useProductSibling(() => props.product)

const comparing = ref(false)
const wishlisted = ref(false)

function compare() {
  comparing.value = !comparing.value
  emit('compare', props.product)
}

function wishlist() {
  wishlisted.value = !wishlisted.value
  emit('wishlist', props.product)
}
</script>

<template>
  <Card :surface="surface" :class="['group', props.class]">
    <CardImage class="relative">
      <div data-slot="product-card-featured-image" class="fui-product-card-featured-image overflow-hidden">
        <Image :src="cover" :alt="product.name" class="aspect-square w-full transition-transform duration-500 ease-out group-hover:scale-105" />
      </div>
      <Button
        :variant="wishlisted ? 'solid' : 'outline'"
        color="neutral"
        size="icon-sm"
        class="absolute top-3 inset-s-3"
        :aria-label="wishlisted ? wishlistedLabel : wishlistLabel"
        :aria-pressed="wishlisted"
        @click="wishlist"
      >
        <HeartIcon data-icon />
      </Button>
      <div v-if="product.badge || product.clearout || product.inStore" class="absolute top-3 inset-e-3 flex flex-col items-end gap-1">
        <Badge v-if="product.badge" variant="new">{{ product.badge }}</Badge>
        <Badge v-if="product.clearout" variant="discount">{{ clearoutLabel }}</Badge>
        <Badge v-if="product.inStore" variant="success">{{ inStoreLabel }}</Badge>
      </div>
    </CardImage>

    <CardContent class="flex flex-col gap-4">
      <div class="flex flex-col gap-2">
        <div class="flex flex-col gap-1">
          <p v-if="product.brand" data-slot="meta" class="fui-meta">
            {{ product.brand }}
          </p>
          <h3 data-slot="title" class="fui-title">{{ product.name }}</h3>
        </div>
        <p v-if="product.description" data-slot="product-card-featured-description" class="fui-product-card-featured-description">
          {{ product.description }}
        </p>
      </div>

      <SwatchGroup v-if="product.siblings?.items?.length" v-model="selectedSibling" type="single" size="xs" shape="pill" label-class="sr-only">
        <SwatchGroupItem
          v-for="sibling in product.siblings.items.slice(0, 5)"
          :key="sibling.key"
          :value="sibling.key"
          :hex="sibling.colors?.exactColorHex"
          :label="sibling.colors?.exactColorName ?? sibling.name"
        />
      </SwatchGroup>

      <div class="flex items-center justify-between gap-4">
        <Price size="lg" :price="product.price" :locale="locale" :show-discount="false" />
        <Button :variant="comparing ? 'solid' : 'outline'" color="neutral" size="sm" :aria-pressed="comparing" @click="compare">
          <CheckIcon v-if="comparing" data-icon />
          <ArrowRightLeftIcon v-else data-icon />
          {{ comparing ? comparingLabel : compareLabel }}
        </Button>
      </div>
    </CardContent>
  </Card>
</template>
```

`components/sections/product-card-featured/types.ts`

```ts
export type { Product, ProductSibling } from '@library/lib/product'
```
