Masonry
Column-balancing masonry layout with SSR-deterministic distribution and optional virtualization.
| Package | @vizejs/ui/masonry |
| Maturity | stable |
| Own the source | vize lib pull masonry |
| Requires | — |
| Aliases | masonry grid, pinterest layout, waterfall layout, column balancing |
| Covers | Masonry.js, react-masonry-css, Mantine Masonry, CSS grid-lanes |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Masonry } from "@vizejs/ui/masonry";
Or copy the source into your project with vize lib pull masonry (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Photo wall balanced into three columns using estimated heights on the server. -->
<script setup lang="ts">
import { Masonry } from "@vizejs/ui/masonry";
interface Photo {
readonly id: string;
readonly caption: string;
readonly height: number;
}
const photos: readonly Photo[] = [
{ id: "dunes", caption: "Dunes at dawn", height: 180 },
{ id: "harbor", caption: "Harbor lights", height: 120 },
{ id: "forest", caption: "Forest trail", height: 240 },
{ id: "glacier", caption: "Glacier edge", height: 150 },
{ id: "market", caption: "Night market", height: 200 },
];
function estimate(photo: Photo): number {
return photo.height;
}
function keyOf(photo: Photo): string {
return photo.id;
}
</script>
<template>
<Masonry :items="photos" :columns="3" :gap="12" :estimate-height="estimate" :get-key="keyOf">
<template #item="{ item }">
<figure>
<figcaption>{{ item.caption }}</figcaption>
</figure>
</template>
</Masonry>
</template>
API
Masonry<Item>
Source: src/families/layout/masonry/masonry.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items (required) |
readonly Item[] |
required |
Items to lay out. |
columns |
number |
3 |
Number of columns. |
gap |
number |
16 |
Gap between items and columns in CSS pixels. |
estimateHeight |
(item: Item, index: number) => number |
() => 200 |
Height estimate used for the server-rendered (and first client) distribution and for items that have not been measured yet. |
getKey |
(item: Item, index: number) => MasonryKey |
(item, index) => index |
Stable key per item; measured heights are remembered by key. |
virtualize |
boolean |
false |
Render only items near the viewport. The root becomes the scroll container, so give it a height. |
overscan |
number |
400 |
Extra pixels rendered above and below the viewport when virtualizing. |
Slots
| Slot | Slot props | Description |
|---|---|---|
item |
MasonryItemSlotState<Item> |
Renders one item. Receives the item, its index, and its column. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
measure |
— | |
layout |
— |
Behavior
Normative state x input -> outcome table for masonry.vue (@vizejs/ui/masonry).
Rows are proven by masonry-layout.test.ts, masonry.test.ts, and
masonry-ssr.test.ts; compile-only assertions live in masonry.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| MS1 | heights, columns | compute layout | each item goes to the shortest column (leftmost on ties) with gap-aware offsets | appends each item to the shortest column with leftmost tie-breaking |
| MS2 | invalid input | compute layout | invalid heights/gaps count as 0; non-positive or fractional columns throw VIZE_UI_MASONRY_COLUMNS |
treats invalid heights and gaps as zero and rejects invalid column counts |
| MS3 | layout, window | visible items | items intersecting the window (plus overscan) are selected in source order | selects items intersecting a window with overscan |
| MS4 | estimated heights | render | balanced flex columns render the item slot with item, index, and column |
distributes estimated heights into balanced columns with slot state |
| MS5 | mounted | measure real heights | measured heights (by key) replace estimates and columns rebalance | rebalances after measuring real heights |
| MS6 | virtualized | scroll | only items near the viewport render, absolutely positioned inside a full-height sizer | virtualizes items near the scroll viewport |
| MS7 | invalid prop | columns: 0 |
the component falls back to one column instead of throwing | falls back to one column for invalid counts |
| MS8 | SSR | isolated requests / hydrate | the estimated distribution is byte-identical and hydrates without warnings | renders a byte-identical estimated distribution across SSR requests, hydrates the estimated distribution without mismatch warnings |
| MS9 | DOM/SSR/Vapor | compile | the SFC compiles in every renderer lane | scripts/check-renderers.ts |