Skip to main content

MasonryGrid

Multi-column layout that places direct element children using shortest-column packing: each tile goes on the column with the least stacked height after measurement (including gaps). Items are still declared in DOM order; only which column they appear in changes. Composition uses kittl-masonry-column internally.

The live preview uses three columns with one 300px-tall cell and nine 100px cells. --spacing-8:0 removes gaps so column packing is easy to read; by default the grid uses 8px gaps between tiles and columns.

Preview
Loading preview…

Import

// Web Component (registers kittl-masonry-grid)
import '@kittl/ui';
// Or tree-shake the element only:
import '@kittl/ui/MasonryGrid';

// React
import { MasonryGrid } from '@kittl/ui-react';

Usage

Web Component

Place element nodes as direct children. The host assigns slot names so each child projects into a column.

<kittl-masonry-grid column-count="3">
<article>First cell</article>
<article>Second cell</article>
<article>Third cell</article>
</kittl-masonry-grid>

Example — 300px tile plus nine 100px tiles (add box-sizing:border-box and width:100% on tiles if you rely on borders or padding):

<kittl-masonry-grid column-count="3" style="width:100%">
<div style="height:300px">1</div>
<div style="height:100px">2</div>
<div style="height:100px">3</div>
<div style="height:100px">4</div>
<div style="height:100px">5</div>
<div style="height:100px">6</div>
<div style="height:100px">7</div>
<div style="height:100px">8</div>
<div style="height:100px">9</div>
<div style="height:100px">10</div>
</kittl-masonry-grid>

React

Composition only: map your data (or wrap static nodes), set a stable key on each sibling, and pass them as children. Same behavior as placing elements under <kittl-masonry-grid> in HTML.

<MasonryGrid columnCount={3}>
{items.map((item) => (
<Card key={item.id}>{item.title}</Card>
))}
</MasonryGrid>

Tree-shake the binding only:

import { MasonryGrid } from '@kittl/ui-react/MasonryGrid';

Props / attributes

Property / attributeTypeDefaultDescription
columnCount / column-countnumber2Number of columns (minimum applied: 1)

Shadow DOM

  • part="root" — row flex wrapper for columns (uses design-token gap via kittl-masonry-column).

Layout notes

  • On first paint the host omits layout-ready until the first successful shortest-column pass, then reveals the grid with a short opacity transition (avoids a visible jump from seeding new slots). The attribute resets when the element reconnects or column-count changes—it is not cleared when you append more tiles (e.g. infinite scroll), so the grid does not blink on each batch. A stable empty grid sets layout-ready on a queueMicrotask boundary so synchronous child inserts in the same turn are still measured before reveal. Tile slots are reseeding only for children that lack a valid c-* for the current column count; existing packed items keep their slots until the shortest-column pass rebalances.
  • Placement is height-aware and reflows on resize or when tile size changes (for example images loading). Visual order is no longer strict left-to-right by DOM index—labels 1–10 may appear in different columns than a simple round-robin would place them.
  • Uneven column bottoms can still happen with indivisible tile heights; shortest-column only minimizes that gap.
  • Use light-DOM element children only; avoid relying on raw text nodes between items.