Skip to content
Compositions

CardGrid

Responsive @container list that lays cards out in rows. The default wrap layout is a wrapping flex row whose items resize at the ProductCard small/medium/full breakpoints via the @container/card-grid query name; layout="fill" is a grid whose columns stretch to fill each row.

Usage

import { calculateColumns, CARD_GRID, CARD_GRID_FILL_MIN_WIDTH, CARD_GRID_RESPONSIVE, CardGrid, cardGridFillColumns, CardGridItem, CardGridItemProps, CardGridItemSize, CardGridProps, CardGridRowMetrics, CardGridRows, CardGridRowsProps, CardGridSkeletonItem, chunkIntoRows, getCardGridMetrics } from "@instruments/materia-ui/compositions/card-grid";

Best practices

  • Render CardGridItem children so each card is a list item under this <ul> - wrapping arbitrary elements loses the list semantics.

  • Use layout="fill" wherever the grid's width changes with the page (a docked panel, a collapsing sidebar): fixed widths leave a ragged gap at the right edge, fill columns don't. Keep CardGridRows on the default, as its row widths come from the fixed metrics.

  • Give the loading grid the same layout and minItemSize as the loaded one, so skeletons and cards share columns and nothing reflows when data lands.

  • Keep this as the direct child of CardGrid so the list semantics stay intact; put the actual card inside it.

  • Chunk items with chunkIntoRows (using calculateColumns) before passing rows; provide a stable getItemKey (and getRowKey where rows reorder) so React reconciliation stays correct.

  • Render one CardGridSkeletonItem per expected card while data loads, inside the same CardGrid - this keeps list semantics and column widths stable across the loading→loaded transition.

Props

CardGrid

PropTypeDefaultDescription
layout"fill" | "wrap""wrap"

"wrap" (default): a wrapping flex row of fixed-width cards that step through the ProductCard breakpoints, leaving any leftover width at the row's end. "fill": a grid whose columns stretch to share the row, so the right edge always lines up with the content column.

minItemSizeCardGridItemSize"base"

Narrowest a card may get before the row drops a column: sm 176px, base 208px, lg 272px. Phones keep two cards a row regardless.

refRef<HTMLUListElement>

CardGridItem

PropTypeDefaultDescription
refRef<HTMLLIElement>

Allows getting a ref to the component instance. Once the component unmounts, React will set ref.current to null (or call the ref with null if you passed a callback ref).

CardGridRows

PropTypeDefaultDescription
getItemKey(required)(item: T) => string
getRowKey(row: T[], rowIndex: number) => string
metrics(required)Omit<CardGridRowMetrics, "rowWidth">
renderAfterRow(props: { metrics: CardGridRowMetrics; row: T[]; rowIndex: number; }) => ReactNode
renderItem(required)(props: { index: number; item: T; row: T[]; rowIndex: number; }) => ReactNode
rows(required)T[][]

CardGridSkeletonItem

PropTypeDefaultDescription
classNamestring

Also exports

  • constcalculateColumns
  • constCARD_GRID
  • constCARD_GRID_FILL_MIN_WIDTH
  • constCARD_GRID_RESPONSIVE
  • constcardGridFillColumns
  • interfaceCardGridItemProps
  • typeCardGridItemSize
  • typeCardGridProps
  • interfaceCardGridRowMetrics
  • interfaceCardGridRowsProps
  • constchunkIntoRows
  • constgetCardGridMetrics

Composition

Builds on

ProductCard