Product Card
The product tile for collection grids and rails, with swatches, quick add and a wishlist heart. Handles sold-out sizes, price ranges and failed adds.
Product Card is the tile you put in a collection grid, search results or a recommendations rail. It takes a Product, the same model Variant Picker uses, and shows one card per product, never one per color. The whole card is a single link on the title, while the heart, swatches and quick add stay separate buttons on top of it.
Colors show as a row of small swatches, all of them reachable. On a narrow card the row scrolls sideways with the last swatch cut off and the edge faded, so shoppers can tell there are more. Hovering a swatch previews that color’s photo after a short pause and preloads it, so the crossfade never shows a blank frame. Clicking it picks the color: the price, badges and stock follow it, and the link opens the product page with ?color= set. On a mouse, hovering the card crossfades to a second photo.
Quick add fits the product. A product with one variant gets an Add to bag button that keeps its width while it changes to a spinner, Added or Couldn’t add. A product with sizes shows a row of size buttons that slides up over the photo on hover or keyboard focus, with sold-out sizes crossed out. On touch screens a + button opens a bottom sheet with Variant Picker instead. A sold-out product hides quick add, shows a Sold out badge and a Notify me link.
Badges come from the data: Sale −20%, Only 3 left, Pre-order and Sold out, plus your own such as New, two at most. The wishlist heart saves optimistically, pops once when saved, and rolls back with a message if your callback throws. ProductGrid lays cards out in 2, 3 or 4 columns and animates between them, and ProductCardSkeleton matches the card’s shape exactly for loading states.
Obtén HextaUI Pro
Un solo pago desbloquea todos los bloques, incluidos los futuros. Ver precios.
Añade el registro Pro a components.json
components.json Añade tu token
Crea un token en tu página de cuenta y colócalo en
.env.localcomoHEXTAUI_PRO_TOKEN.Añade el bloque
pnpm dlx shadcn@latest add @hextaui-pro/product-card
A Shopify collection
Map each Storefront API product once and pass it in. onQuickAdd gets the chosen variant, so adding to the cart is one fetch. If your route throws, its message shows on the card where the shopper clicked.
A recommendations rail
Put compact cards in a horizontally scrolling list for “You may also like” or “Complete the look”. Each card keeps its own swatches and quick add, so the rail needs no extra state.
A wishlist saved to your API
The heart fills right away and rolls back if the request fails. Signed-out shoppers save to localStorage, so saving never needs an account, and signed-in shoppers save to your API.
Anatomía
Las partes que compones, de fuera hacia dentro.
| Parte | Descripción |
|---|---|
ProductCard | Photo, badges, wishlist heart, quick add, swatches, vendor, title, price and rating. |
ProductGrid | A responsive list of cards with 2, 3 or 4 columns and a loading state. |
ProductCardSkeleton | A loading placeholder with the card’s exact geometry. |
getAutoBadges, getCardPrice, getQuickAddMode, getSizeChips | The pure helpers behind the card, usable on the server. |
ProductCard
Also takes article props.
| Prop | Tipo | Predeterminado |
|---|---|---|
productThe product with its options, variants and images. | Product | – |
hrefWhere the card links. A string gets ?color= added once the shopper picks a color. | string | (color) => string | "/products/{handle}" |
linkRenderRender the title link with your router’s Link, such as <Link prefetch />. | ReactElement<a> | – |
selectedColorThe chosen swatch value when controlled. The swatch option is the one with swatches. | string | – |
defaultSelectedColorThe starting swatch value. Defaults to the first color in stock. | string | – |
onSelectedColorChangeCalled when the shopper picks a color. | (color: string) => void | – |
badgesYour own badges, shown after the automatic ones. | (string | { id; label; tone? })[] | – |
autoBadgesShow Sale, Low stock, Pre-order and Sold out from the data. | boolean | true |
maxBadgesThe most badges shown at once. | number | 2 |
ratingShows stars with “4.8 (126)”. Hidden when count is 0. | { value: number; count: number } | null | – |
wishlistedWhether the product is saved, when controlled. | boolean | – |
defaultWishlistedThe starting saved state when uncontrolled. | boolean | false |
onWishlistChangeShows the heart. The heart changes right away and rolls back if this throws or rejects. | (wishlisted, product) => void | Promise<void> | – |
onQuickAddShows quick add. The card shows pending, added and error states from the returned promise. | (variant, product) => void | Promise<void> | – |
notifyHrefWhere Notify me links for a sold-out product. Defaults to the product link with ?notify=1. | string | – |
sizeCard density. Inside ProductGrid it follows the column count. | "sm" | "default" | – |
priorityLoad the photo eagerly with high priority, for cards above the fold. | boolean | false |
imageSizesThe sizes attribute for the photo. | string | – |
headingLevelThe heading level of the title. | 2 | 3 | 4 | 3 |
localeLocale for prices and counts. | Intl.LocalesArgument | "en-US" |
trailingZerosShow $48.00 instead of $48. | boolean | false |
labelsWords for buttons, badges, swatch names and announcements. | Partial<ProductCardLabels> | – |
ProductGrid
Also takes ul props. Each child is wrapped in a list item.
| Prop | Tipo | Predeterminado |
|---|---|---|
columnsColumns on wide screens. Phones show 1 column for 2 and 2 columns otherwise. | 2 | 3 | 4 | 3 |
sizeCard density. Defaults to sm at 4 columns. | "sm" | "default" | – |
loadingShow skeletons and set aria-busy. | boolean | false |
skeletonCountHow many skeletons to show while loading. | number | 8 |
skeletonA custom skeleton for each loading cell. | ReactNode | – |
ProductCardSkeleton
Also takes div props.
| Prop | Tipo | Predeterminado |
|---|---|---|
sizeMatch the card density. | "sm" | "default" | – |
swatchesInclude the swatch row. | boolean | true |
ratingInclude the rating line. | boolean | false |
| Key | Acción |
|---|---|
| Tab | Moves from the product link to the heart, the quick add buttons and each swatch. Focusing the card reveals the size row. |
| Enter | Opens the product page from the link, or adds the focused size. |
| Space | Toggles the heart, picks the focused swatch or adds the focused size. |
| Escape | Closes the quick add sheet and returns focus to the + button. |
- The card is one link named by the product title, inside a heading. The heart, swatches and quick add are separate buttons layered above it, never nested inside the link.
- Swatches are named like “Sage, 3 of 5” and say when a color is sold out. The chosen one has aria-pressed. Sold-out sizes stay focusable with aria-disabled and are named “Twin, sold out”.
- The heart uses aria-pressed and the label “Save Ring Vase to wishlist”. A polite live region announces saves, removals and adds, such as “Added Washed Linen Duvet Cover, White / King, to your bag”, and failures are announced as alerts.
- Hover-only effects are limited to mouse pointers, and the size row also opens on keyboard focus. Motion is transform and opacity only, and reduced motion keeps only short fades.
Construido con
Los componentes gratuitos de HextaUI con los que está hecho Product Card. Cada uno se instala por separado.
Código
6 archivos, añadidos a components/blocks/product-card.