Skip to content

React Component ​

Renders one measured wrapper <div> per item around whatever children returns for it — item content stays entirely yours, this only measures and positions the wrapper. With options.virtualize on, only the currently visible items (± overscan) actually get a wrapper/render call at all — the rest exist only as an estimate inside the engine, not in the DOM. See Virtualization.

tsx
<MasonryGrid items={items} options={{ columns: 'auto', minLaneSize: 240 }}>
  {(item) => <MyCard data={item} />}
</MasonryGrid>

Props ​

items ​

MasonryGridItem[] · required

Same shape as core's MasonryItemDescriptor (see Items & Bento Spans), minus el — the component creates and owns each item's wrapper element itself. Every field is optional, including id: id, colSpan, rowSpan, aspectRatio, estimatedSize, order.

If id is omitted, the component generates a stable one keyed by the item object's own reference (a WeakMap) — the same object in the array keeps the same id across any re-render. If items gets rebuilt into fresh objects on every render instead (e.g. .map()-ing over source data each time), each fresh object gets a newly generated id — give those an explicit id from your own data instead.

options ​

MasonryOptions · optional, default {}

Passed straight through to createMasonryEngine() — see Engine API. Core has no live updateOptions(), so a changed options content (compared structurally via optionsEqual, not just by reference — the component memoizes it internally) tears down and recreates the engine. You don't need to useMemo it yourself for this reason, though doing so avoids the internal comparison work on every render.

sortable ​

boolean · default: false

Turns every item wrapper into a keyboard-reorderable item — see Sortable below.

onLayout ​

(items: MasonryItemLayout[]) => void · optional

Called on every engine relayout, with every item's resolved box.

onReorder ​

(items: MasonryGridItem[]) => void · optional

Called when the user reorders an item via keyboard (sortable only), with the reordered array — see Sortable below. <MasonryGrid> never mutates items itself; applying the new order to your own state is this callback's job.

children ​

(item: MasonryGridItem) => ReactNode · required

Called once per item in items (or once per currently-visible item, under virtualize) — the React equivalent of the Vue adapter's #item slot. A render-prop function, not a plain JSX child: <MasonryGrid>{someElement}</MasonryGrid> is wrong; it must be <MasonryGrid>{(item) => someElement}</MasonryGrid>.

Sortable ​

tsx
import { useState } from 'react'
import { MasonryGrid } from '@macrulez/masonry-kit-react'

function Board() {
  const [items, setItems] = useState([{ id: 'a' }, { id: 'b' }, { id: 'c' }])

  return (
    <MasonryGrid items={items} sortable onReorder={setItems}>
      {(item) => <MyCard data={item} />}
    </MasonryGrid>
  )
}

With sortable on, every item wrapper becomes keyboard-operable — identical behavior to the Vue component's own sortable (see MasonryGrid Component for the full key-by-key breakdown: space/enter to pick up or drop, arrow keys to move, escape to cancel). The only difference is the callback shape: React calls onReorder(items) instead of emitting a Vue event, and never mutates items on its own — onReorder is where you apply the new order to your own state, same as setItems above.

The same ARIA attributes (role="button", aria-roledescription, aria-pressed, aria-describedby) and visually-hidden live-region announcements apply as the Vue component. Pointer/touch drag is deliberately not implemented — see Overview for why.