Product cards
Products in an answer when someone asks what to buy: a side-by-side comparison, a scrolling row of results, one top pick in detail, a spec table that can show only the differences, and a product named inline with a hover card.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/product-card-default.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/product-card-default.json
Usage & API
Example
import { ProductComparison } from "@/components/ai/product-card-default"
<ProductComparison
products={[]}
recommendedId="…"
saved={[]}
onToggleSave={() => {}}
/>Props ProductComparison
| Prop | Type & description | Required |
|---|---|---|
products | ComparedProduct[] | Required |
recommendedId | string | Required |
saved | string[] | Required |
onToggleSave | (id: string) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Comparison lays the products in a row that snaps sideways on a narrow width and becomes three columns from the small breakpoint. Every card shows a drawn shot, a badge reading Best for and the reason, a bookmark, the maker, the rating and review count, the name, spec chips, a why sentence, and the price with at and the retailer. The recommended id gets an indigo border and an indigo badge. The others keep a light badge. The bookmark's pressed state is save, and the sample starts with one product saved. The prompt is noise cancelling headphones for a 14 hour flight to Tokyo, under $350. The reply names the Arden QH-7, which is the recommended card, best for flights, $329 at Best Buy, rated 4.7 from 2140 reviews. Beside it are the Lumen Audio Studio Pro 2, best for music, $279, and the Sola Air 3, best for value, $149. The why lines compare engine noise, sound and weight. Saving toggles the bookmark and does not otherwise change the card.
Example
import { ResultsRow } from "@/components/ai/product-card-carousel"
<ResultsRow
products={[]}
saved={[]}
onToggleSave={() => {}}
/>Props ResultsRow
| Prop | Type & description | Required |
|---|---|---|
products | ResultProduct[] | Required |
saved | string[] | Required |
onToggleSave | (id: string) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Results row is a horizontal snap scroller labelled Results, with previous and next buttons once the row can actually move that way. The buttons are hidden on the smallest width, where the person scrolls. A card shows the drawing, an optional sale flag with the dollar amount off, a heart, the maker and name, the rating, the price, a struck previous price when there is one, and the retailer. The heart says Save or Unsave. Opening a card raises a dialog with the summary, a spec grid, the price, a line that the price is the lowest in 90 days when a previous price exists, a retailer link, and Close. Escape closes it and returns focus to the card that opened it. The sample answers robot vacuums for dog hair on hardwood and shows six machines. Three have a previous price, so three sale flags appear. The dialog is the only place the summary and the spec pairs are visible.
Example
import { TopPick } from "@/components/ai/product-card-pick"
<TopPick
maker="…"
name="…"
rating={1}
reviews={1}
pros={[]}
cons={[]}
specs={[]}
offers={[]}
alternatives={[]}
/>Props TopPick
| Prop | Type & description | Required |
|---|---|---|
maker | string | Required |
name | string | Required |
rating | number | Required |
reviews | number | Required |
pros | string[] | Required |
cons | string[] | Required |
specs | [string, string][] | Required |
offers | RetailerOffer[] | Required |
alternatives | Alternative[] | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Top pick is one product. A large drawing sits over three photo tabs, Front, Three quarter and Group head, and the selected thumb keeps an indigo edge. Beside that are the maker, the name, the rating, the review count, a pros list with plus icons, a cons list with minus icons, and a strip of spec pairs. Under the fold, each offer is a retailer, a stock dot, a note, the price, and a view link. Stock reads In stock, Low stock or Backorder. The cheapest offer is marked Lowest and tinted. A Compare with alternatives row expands names, one-line comparisons and prices. The sample is the Halden Bar 2 espresso machine, 4.6 from 1874 reviews, praised for a 45 second heat-up, a steam wand that works during a shot, and a 30-setting grinder, with a drip tray that fills fast and a loud grinder. Offers are Williams Sonoma at $649 in stock, Amazon at $599 low stock, and Crate & Barrel at $629 on backorder, so Amazon wears Lowest. The alternatives are the Ferro Duo at $499 and the Caffo Lite at $349.
Example
import { SpecTable } from "@/components/ai/product-card-specs"
<SpecTable
products={[]}
rows={[]}
onlyDifferences={true}
onOnlyDifferencesChange={() => {}}
/>Props SpecTable
| Prop | Type & description | Required |
|---|---|---|
products | SpecProduct[] | Required |
rows | SpecRow[] | Required |
onlyDifferences | boolean | Required |
onOnlyDifferencesChange | (on: boolean) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
Implementation notes
Spec table is a grid labelled Monitor comparison. The toolbar says how many monitors and how many specs, and hosts a switch, Show only differences. Turning it on hides rows whose cell text is identical and appends how many identical rows are hidden. Product headers stick, with a small drawing, the name, the price and the retailer. A cell with the best score on its row is tinted green. If any score is missing, or every cell ties, nothing on that row is marked. The sample compares the Corvo 27Q at $299, the Nimbus U27 4K at $449, the Kestrel 27 OLED at $699 and the Atlas 34 UW at $549, across price, resolution, refresh, panel, brightness, colour, USB-C power, stand, VESA, speakers, weight and warranty. The reply points at the Nimbus for text and the Kestrel for games. VESA, speakers and warranty match on all four, so those are the rows the switch hides. The stand stays, because the ultrawide's stand text differs.
Example
import { ProductMention } from "@/components/ai/product-card-mention"
<ProductMention
product={product}
open={true}
onOpenChange={() => {}}
/>Props ProductMention
| Prop | Type & description | Required |
|---|---|---|
product | MentionedProduct | Required |
open | boolean | Required |
onOpenChange | (open: boolean) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Inline mention is a chip in the sentence: a tiny drawing and the product name. Hover waits a quarter of a second, focus opens immediately, and a click toggles. The peek is a tooltip with a larger drawing, the maker, the name, the rating, the review count, a short note, the price, the retailer and a View link. It flips to the right when it would overflow the host marked as the mention host. Escape closes it. The sample answers an open-office keyboard complaint. The prose recommends the Mosswood 75 Silent at $139, silent linear, 4.7 from 1312 reviews at Amazon, then the Halvard K2 Low Profile at $99, and the Tern Slim at $59 as the cheap scissor-switch option. Only one peek is open, because the sample stores a single id. There is no save control on the chip.
Choosing a layout
Use the comparison when there are a few serious options and you can say what each is best for, in a badge and one sentence. Use the results row when there are more hits than fit, some of them on sale, and the detail should open on demand rather than sit inline. Use the top pick when the advice is a single name plus where to pay and what you give up. Use the spec table when the decision is a grid of attributes, especially if many rows are identical and should get out of the way. Use the mention when the reply is a paragraph and the product is a noun in it.
A data table is the better fit when the rows are records from a query, with sorting and a CSV file, not a rating and a retailer. A chart is the better fit for a numeric series. Weather, itinerary, email draft and calendar event are the other rich answers in this catalog, and none of them are a catalog of goods. Do not force a forecast or a meeting into a product card because it has a title and a button. If the person asked what to buy, pick the product drawing that matches how many items you are willing to stand behind.
Which comparison card is the one being recommended?
The one whose id you pass as recommendedId. It gets the indigo border and the filled Best for badge. The other cards still say Best for, with their own reason, but the badge stays light and the border stays neutral. The sample passes the Arden headphones. The bookmark is separate. A saved product is not automatically the recommendation, and the sample in fact starts with a different card saved.
What does Show only differences hide?
Rows where every cell's text matches the first cell. It does not look at the score, and it does not hide a row just because no winner was highlighted. The header counts how many rows that removes. In the monitor sample, the shared VESA size, the lack of speakers and the three-year warranty disappear, while the stand stays because one monitor's stand text is shorter. Turn the switch off and those rows come back in their original order.