Masonry
A column layout for items of unequal height, built on CSS columns rather than a JS layout pass — so it packs before paint and reflows on resize for free. Items order top-to-bottom within a column, which is the trade.
src/components/ui/masonry.tsxAbout
From the component's own source, where it was written.
A column layout for items of unequal height.
For galleries, note walls, log cards — anything where forcing a uniform row height would crop the content or leave ragged gaps under it.
**CSS columns, not a JS layout pass.** The usual masonry implementation measures every child and assigns it to the shortest column on each render, which costs a layout read per item, has to be redone on resize, and reflows visibly after paint. column-count does the same packing in the layout engine, before paint, and reflows on resize for free.
The trade is real and worth stating: CSS columns order items **top to bottom within each column**, not left to right across them. For a gallery that is fine. For anything where reading order carries meaning — a ranked list, a feed — it is wrong, and Kanban or a plain grid is the honest choice.
break-inside: avoid on each item is what stops a card being sliced in half across a column boundary, which is the default and never what anyone wants.
Install
npx astralyx-ui add masonry
Usage
import { Masonry } from '@/components/ui/masonry'
<Masonry columns={{ base: 1, sm: 2, lg: 3 }} gap={4}>
{items.map((item) => <Card key={item.id}>{item.body}</Card>)}
</Masonry>Composer
Change the props and the source below follows.
Sensor drift
Three probes reported the same 0.4°C offset after the firmware bump.
Retry storm
The queue drained in 90 seconds once the backoff jitter landed.
Cold start
First request after a deploy pays 800ms. Warming halves it, and the warmer costs more than the latency does.
Cache hit rate
94% and steady.
Token spend
The cheap model handles 91% of calls and 11% of the bill, which is the whole argument for routing.
Index size
Two million chunks, 4.1 GB on disk.
<Masonry columns={{ base: 1, sm: 2, lg: 3 }} gap={4}>
{items.map((item) => (
<Card key={item.id}>{item.body}</Card>
))}
</Masonry>Examples
Uneven tiles
<Masonry columns={{ base: 1, sm: 2, lg: 3 }}>{tiles}</Masonry>API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
Masonry
A column layout for items of unequal height.
Extends ComponentProps<'div'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | — |
| columns | { base?: number; sm?: number; md?: number; lg?: number; xl?: number } | { base: 1, sm: 2, lg: 3 } | Columns per breakpoint. Written out rather than interpolated, because Tailwind scans source text and columns-${n} produces nothing at build time. |
| gap | 2 | 3 | 4 | 6 | 8 | 4 | Applied as both the column gap and the item’s bottom margin — CSS columns have no row gap. |
| order | top-to-bottom per column | — | The honest trade for not running a JS layout pass. Fine for a gallery; wrong for a ranked list or a feed, where a plain grid or Kanban is the right answer. |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| break-inside | avoid | Applied to every item, so a card is never sliced across a column boundary — which is the default without it. |