KQ-1335 · PRODUCT/GRID
Product Grid
A filterable product grid whose shelf moves instead of snapping: when a facet chip or the sort changes, cards that stay travel to their new cells on glide, leaving cards shrink and fade on the exit ease and arriving ones scale in — together, as a wave timed by each card's new position, or as a cross-fade — while every chip's count and the result total roll on snap. A card's quick view grows out of the card itself: the dialog's rounded clip opens from the card's box while the product art flies to its place on the same glide progress, or it rises as a sheet or slides in from the side. Facets are roving-focus toggle groups, sort is a listbox, and the quick view is a modal dialog that traps focus, closes on Escape and gives focus back to its card.
16 of 16 · everything · featured · bag 0
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/product-grid
Props
- reflow
- "glide" | "wave" | "fade"
- default "glide"
- How the shelf rearranges: every card glides together, a wave delayed by each card's new position (kept under 600ms), or a cross-fade with no travel.
- quickview
- "morph" | "sheet" | "side"
- default "morph"
- How the quick view arrives: grown out of the card, risen as a bottom sheet, or slid in as a side panel (a sheet below 40rem).
- density
- "compact" | "regular" | "roomy"
- default "regular"
- Columns and how much each card says: compact drops the rating for more columns, roomy adds the description and lays phone cards out sideways.
- products
- ProductGridItem[]
- default defaultProducts
- The shelf: id, name, category, price, compareAt, colour ids, rating, reviews, badge, stock, when it arrived, description and the drawn shape that stands in for its photograph.
- colors
- ProductGridColor[]
- default defaultProductColors
- The glazes products come in: id, name and a pigment tint (any CSS colour, ideally at a fixed lightness).
- priceBands
- ProductGridPriceBand[]
- default defaultPriceBands
- The price chips: id, label, inclusive min and exclusive max (omit it for no ceiling).
- filters / defaultFilters
- ProductGridFilters
- default —
- Categories, colour ids, a price band id and in-stock-only. Controlled or uncontrolled; defaults to nothing set.
- onFiltersChange
- (filters: ProductGridFilters) => void
- default —
- Fires from the chip, or Clear filters, that changed the filters, with the whole new set.
- sort / defaultSort
- "featured" | "price-asc" | "price-desc" | "newest" | "rating"
- default "featured"
- The order of the shelf. Controlled or uncontrolled.
- onSortChange
- (sort: ProductGridSort) => void
- default —
- Fires from the sort menu's choice.
- open / defaultOpen
- string | null
- default null
- The product whose quick view is open. Controlled or uncontrolled.
- onOpenChange
- (id: string | null) => void
- default —
- Fires as a quick view is opened from a card (its id) or closed by Escape, Close or the scrim (null).
- onAddToCart
- (id: string, options: { color: string; qty: number }) => void
- default —
- Quick add on a card (the default colour, one) or Add to bag in the quick view (the chosen colour and quantity).
- onOpen
- (id: string) => void
- default —
- View details in the quick view. Without it there is no link.
- pageSize
- number
- default 12
- Cards shown before Show more. A new filter or sort starts from the first page.
- format
- (amount: number) => string
- default —
- Money. Defaults to Intl currency formatting in locale and currency.
- locale
- string
- default "en-US"
- Locale for the default money format.
- currency
- string
- default "USD"
- Currency for the default money format.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the shelf has arrived: skeleton cards while loading, a message and Try again on error.
- onRetry
- () => void
- default —
- Try again was pressed after the shelf failed to load.
- title
- string
- default "Kitchen"
- The shelf's heading.
- label
- string
- default —
- The region's accessible name. Defaults to the title.
- sound
- boolean
- default false
- A click for every chip, sort choice, glaze, stepper and add; a swish as a quick view opens (higher) and closes (lower).
- disabled
- boolean
- default false
- Browse only: nothing can be filtered, sorted, opened or added.
- className
- string
- default —
- Classes for the root; its scroller is at most 560px tall and the quick view covers the root's visible box.
| Prop | Type | Default | Description |
|---|---|---|---|
| reflow | "glide" | "wave" | "fade" | "glide" | How the shelf rearranges: every card glides together, a wave delayed by each card's new position (kept under 600ms), or a cross-fade with no travel. |
| quickview | "morph" | "sheet" | "side" | "morph" | How the quick view arrives: grown out of the card, risen as a bottom sheet, or slid in as a side panel (a sheet below 40rem). |
| density | "compact" | "regular" | "roomy" | "regular" | Columns and how much each card says: compact drops the rating for more columns, roomy adds the description and lays phone cards out sideways. |
| products | ProductGridItem[] | defaultProducts | The shelf: id, name, category, price, compareAt, colour ids, rating, reviews, badge, stock, when it arrived, description and the drawn shape that stands in for its photograph. |
| colors | ProductGridColor[] | defaultProductColors | The glazes products come in: id, name and a pigment tint (any CSS colour, ideally at a fixed lightness). |
| priceBands | ProductGridPriceBand[] | defaultPriceBands | The price chips: id, label, inclusive min and exclusive max (omit it for no ceiling). |
| filters / defaultFilters | ProductGridFilters | — | Categories, colour ids, a price band id and in-stock-only. Controlled or uncontrolled; defaults to nothing set. |
| onFiltersChange | (filters: ProductGridFilters) => void | — | Fires from the chip, or Clear filters, that changed the filters, with the whole new set. |
| sort / defaultSort | "featured" | "price-asc" | "price-desc" | "newest" | "rating" | "featured" | The order of the shelf. Controlled or uncontrolled. |
| onSortChange | (sort: ProductGridSort) => void | — | Fires from the sort menu's choice. |
| open / defaultOpen | string | null | null | The product whose quick view is open. Controlled or uncontrolled. |
| onOpenChange | (id: string | null) => void | — | Fires as a quick view is opened from a card (its id) or closed by Escape, Close or the scrim (null). |
| onAddToCart | (id: string, options: { color: string; qty: number }) => void | — | Quick add on a card (the default colour, one) or Add to bag in the quick view (the chosen colour and quantity). |
| onOpen | (id: string) => void | — | View details in the quick view. Without it there is no link. |
| pageSize | number | 12 | Cards shown before Show more. A new filter or sort starts from the first page. |
| format | (amount: number) => string | — | Money. Defaults to Intl currency formatting in locale and currency. |
| locale | string | "en-US" | Locale for the default money format. |
| currency | string | "USD" | Currency for the default money format. |
| status | "ready" | "loading" | "error" | "ready" | Whether the shelf has arrived: skeleton cards while loading, a message and Try again on error. |
| onRetry | () => void | — | Try again was pressed after the shelf failed to load. |
| title | string | "Kitchen" | The shelf's heading. |
| label | string | — | The region's accessible name. Defaults to the title. |
| sound | boolean | false | A click for every chip, sort choice, glaze, stepper and add; a swish as a quick view opens (higher) and closes (lower). |
| disabled | boolean | false | Browse only: nothing can be filtered, sorted, opened or added. |
| className | string | — | Classes for the root; its scroller is at most 560px tall and the quick view covers the root's visible box. |