Masonry

A column layout for items of uneven height: each item goes into the shortest column rather than onto the next row.

Usage

When to reach for a column-packed wall instead of a grid or a list, how the placement rule decides where a card lands, and the things it leaves to you: keys, gutters, images that have not loaded yet.

When to use

Masonry packs items into columns. Each item drops into whichever column is shortest at that moment, so the bottom edge comes out ragged and there are no gaps inside the wall. A grid lays out rows instead, so one tall cell pushes down everything beside it and leaves space under the short ones.
Use it when the heights are uneven and you do not know them ahead of time: image galleries, note cards, log entries, a wall of quotes of different lengths.
  • Use a Grid when the items are the same height, or should line up in rows. Anything a reader compares across (prices, stats, feature cards) belongs on a shared baseline, and a Grid is also the cheaper layout: no measuring, no absolute positioning, no reflow.
  • Use a Listy when the order matters more than the packing. Masonry keeps the DOM in your array order but paints each item wherever the columns allow, so “first” and “next” stop being things the eye can follow. A ranked list, a feed or a set of steps wants one column and a real sequence.
  • Use CSS columns (or grid-template-columns with a column flow) when you need none of this component: no per-item data, no typed render callback, no re-measure on resize. If a plain column-count already looks right, it is cheaper and it never reflows.

Columns

columns is a number, or an object keyed by breakpoint. It defaults to 3.
  • An object uses the largest breakpoint that matches the screen and has a value (xxxl, xxl, xl, lg, md, sm, xs, in that order), so you only name the widths that change.
  • Name xs. When nothing matches, the object falls back to columns.xs, and to 1 if you did not give one.
  • The breakpoints are the same as Grid's, so lg starts at 992px. A lg: class next to the wall changes at the same width.

Gutter

gutter defaults to 0, so the cards touch until you set it.
  • A number sets both axes. A [horizontal, vertical] pair sets them apart, and leaving the second value out copies the first.
  • Either value can be an object keyed by breakpoint, so each axis can change with the screen on its own: [{ xs: 8, sm: 16, md: 32 }, { xs: 4, sm: 8 }].
  • Responsive gutters need sm. xs only applies below 576px, so { xs: 4, md: 24 } leaves no gap from 576 to 767px. Give every breakpoint you want covered its own key.
  • The horizontal gutter comes out of the card width, so a wider gutter narrows the cards instead of overflowing the container.

items and itemRender

You pass data, not children. items is an array of { key, data } and itemRender turns one entry into a node. Pass your item type to Masonry and data arrives typed, with no any and no cast.
  • Give each item a stable key. An index key makes Masonry re-place the wrong card when the list is filtered or reordered. Use the item's own id.
  • itemRender also receives the item's index and the column it landed in. The column tells you where the item is, and changing it in the callback does not move it.
  • An entry may carry its own children, which replaces itemRender for that entry. Use it for one odd card (a call to action, a promo) in an otherwise uniform list.
  • Masonry positions items and paints nothing, so every border, radius and background in the wall comes from what itemRender returns.

Placement against reading order

The DOM keeps your items order. The screen shows whatever the shortest-column rule produced. The two are different sequences, and there is no prop that lines them up.
  • Items are placed one by one, each into the column that is shortest at that moment. Item 4 can sit above item 3, and reading down one column skips around the array.
  • There is no row-by-row mode. If the sequence has to be readable, pick a different layout.
  • To place a few items yourself, set column on their entries in items. An index past the end lands in the last column. Pin the few items whose position matters (a header card, a pinned note) and let the rest pack.
  • Sorting the array still works for anything read one item at a time: newest first stays newest first in the DOM, in the tab order and to a screen reader. Only the two-dimensional scan goes ragged.
  • onLayoutChange reports each item's column once every item has a position. Save it back onto each item as column to keep cards where they are when items are added or removed.

Images and heights you do not know yet

Masonry measures rendered elements, so an image that has not loaded yet measures at its current box, usually zero. The first packing is wrong until the images arrive.
  • Masonry re-measures when an image inside it loads or fails, so a plain <img> in a card needs nothing extra.
  • Give the image an aspect ratio anyway when you know it. A card that has its final height before the image loads never re-packs, and re-packing is what makes a gallery jump under the reader.
  • A background image, a font swap or a widget that renders late fires no load event. Turn on fresh for those: it watches every item for resize and re-packs, at the cost of cards moving more often.
  • Without fresh, a card that grows in place (an expanded answer, a revealed detail) is not re-measured, and the card below it can end up overlapped. Turn it on when you see that, not by default.

How many columns is too many

Pick the column count for reading, not for density. Every column you add shortens the run a reader can follow before their eye has to jump back up.
  • Past four or five columns the wall stops reading as a set of cards and starts reading as texture. That suits a photo wall, not cards with text.
  • Below about 240px a column cannot hold a card with a title, a line of body and a footer without wrapping all of them. Divide the container width by the count you want before you commit to it.
  • One column is a valid answer at xs, and it is the fallback when nothing matches. A single-column Masonry is a stack, which is right on a phone.
  • The ragged bottom edge grows with the column count. With many columns and few items, the columns end at very different heights.