# Product Detail

> A complete product page section built from Product Gallery and Variant Picker. The buying column sticks while photos scroll: breadcrumbs, rating, a price that follows the selection with savings and installments, a quantity stepper, add to cart that confirms in place and turns into Notify me when sold out, a wishlist heart, perks and expanding detail sections.

- Slug: `product-detail`
- Category: [Commerce](https://www.assembli.design/category/commerce.md)
- Type: block
- Tags: product-page, pdp, ecommerce, add-to-cart, gallery, variants, wishlist, shop
- npm dependencies: lucide-react, motion
- shadcn/ui dependencies: @assembli/product-gallery, @assembli/variant-picker
- Live preview: https://www.assembli.design/preview/product-detail
- Page: https://www.assembli.design/components/product-detail

![Product Detail preview](https://www.assembli.design/thumbnails/product-detail-light.png)

## Props

Other props pass through to the root element.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `string` |  |  |
| `eyebrow` | `React.ReactNode` |  | A line above the title: the brand or collection. |
| `breadcrumbs` | `{ label: string; href: string }[]` |  |  |
| `images` (required) | `GalleryImage[]` |  |  |
| `price` (required) | `number \| ((selection: VariantSelection) => number)` |  | Price for a selection; a number when it never changes. |
| `compareAt` | `number \| ((selection: VariantSelection) => number \| undefined)` |  | A struck-through original price for a selection. |
| `installments` | `number` |  | Split the price into this many interest-free payments under it. |
| `currency` | `string` | `"USD"` |  |
| `locale` | `string` | `"en-US"` |  |
| `rating` | `{ value: number; count: number; href?: string }` |  |  |
| `description` | `React.ReactNode` |  |  |
| `options` | `VariantOption[]` | `[]` |  |
| `defaultSelection` | `VariantSelection` |  |  |
| `stockOf` | `(selection: VariantSelection) => number \| undefined` |  |  |
| `imageFor` | `(selection: VariantSelection, changed: string) => number \| undefined` |  | Gallery image to show when an option changes, e.g. jump to the photo of a color. |
| `shipping` | `React.ReactNode` |  | Shown beside "In stock". |
| `perks` | `ProductPerk[]` |  |  |
| `sections` | `ProductSection[]` |  |  |
| `onAddToCart` | `(selection: VariantSelection, quantity: number) => void \| Promise<unknown>` |  | Return a promise to show "Adding…" until it settles. |
| `onNotify` | `(selection: VariantSelection) => void` |  |  |
| `saved` | `boolean` |  | Saved to a wishlist. |
| `onSavedChange` | `(saved: boolean) => void` |  |  |
| `maxQuantity` | `number` | `10` |  |

## Install

One-time setup (needs a subscription):

1. Add the registry to the project's `components.json`: `{"@assembli":{"url":"https://www.assembli.design/r/{name}.json","headers":{"Authorization":"Bearer ${ASSEMBLI_API_KEY}"}}}` (merge into `"registries"`).
2. Put an API key from https://www.assembli.design/account in `.env.local` as `ASSEMBLI_API_KEY=asm_…`.

Then install with the shadcn CLI (writes `components/product-detail/product-detail.tsx` and installs dependencies):

```bash
npx shadcn@latest add @assembli/product-detail
```

Registry item JSON: https://www.assembli.design/r/product-detail.json (send `Authorization: Bearer <api key>`)

## Code

> Component source needs an Assembli subscription ($12/month or $96/year) and an API key sent as `Authorization: Bearer <key>`. To get one without copying anything, POST https://www.assembli.design/api/agent/login (or call the MCP tool connect_account), show the user the link it returns, and poll https://www.assembli.design/api/agent/token for the key. Or subscribe at https://www.assembli.design/pricing and create a key at https://www.assembli.design/account.
