🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@virtuoso.dev/masonry

Package Overview
Dependencies
Maintainers
2
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@virtuoso.dev/masonry - npm Package Compare versions

Comparing version
1.3.5
to
1.4.0
+11
-0
CHANGELOG.md
# Change Log - @virtuoso.dev/masonry
## 1.4.0
### Minor Changes
- [#1318](https://github.com/petyosi/react-virtuoso/pull/1318) [`8d47a4d`](https://github.com/petyosi/react-virtuoso/commit/8d47a4d8a7ecdd4caa36eb336b6921738d73f425) Thanks [@petyosi](https://github.com/petyosi)! - Updated API reference
### Patch Changes
- Updated dependencies [[`8d47a4d`](https://github.com/petyosi/react-virtuoso/commit/8d47a4d8a7ecdd4caa36eb336b6921738d73f425)]:
- @virtuoso.dev/gurx@1.2.0
## 1.3.5

@@ -4,0 +15,0 @@

+53
-20

@@ -1,21 +0,12 @@

/* Excluded from this release type: ComputeItemKey */
/**
* Used for the custom components that accept the masonry context prop.
*/
export declare type ContextAwareComponent<Context = any> = React.ComponentType<{
/**
* The value currently passed to the `context` prop of the `VirtuosoMasonry` component.
*/
context: Context;
}>;
/* Excluded from this release type: Data */
/**
* A React component that's used to render the individual item.
* A React component that's used to render the individual masonry item.
* @typeParam Data - The type of items in the data array.
* @typeParam Context - Optional contextual data passed from the parent component.
* @group VirtuosoMasonry
*/
export declare type ItemContent<Data = any, Context = any> = React.ComponentType<{
/**
* The value of the `context` prop passed to the list.
* The value of the `context` prop passed to the masonry.
*/

@@ -28,3 +19,3 @@ context: Context;

/**
* The index of the item in the list data array.
* The index of the item in the data array.
*/

@@ -38,3 +29,3 @@ index: number;

* The DOM attributes that you can pass to the `VirtuosoMasonry` component to customize the scroll element.
* @noInheritDoc
* @group VirtuosoMasonry
*/

@@ -45,2 +36,9 @@ export declare type ScrollerProps = Omit<React.HTMLProps<HTMLDivElement>, 'data' | 'onScroll' | 'ref'>;

/**
* A React component for efficiently rendering large masonry/Pinterest-style layouts with variable-height items
* distributed across multiple columns. Supports virtualization, window scrolling, and SSR.
* @typeParam Data - The type of items in the data array passed to the component.
* @typeParam Context - Optional contextual data passed to the ItemContent render callback.
* @group VirtuosoMasonry
*/
export declare const VirtuosoMasonry: <Data, Context>(props: VirtuosoMasonryProps<Data, Context> & {

@@ -50,5 +48,17 @@ ref?: NoInfer<React.Ref<Record<string, never>>>;

/**
* Props for the VirtuosoMasonry component.
* @typeParam Data - The type of items in the data array passed to the component.
* @typeParam Context - Optional contextual data passed to the ItemContent render callback.
* @group VirtuosoMasonry
*/
export declare interface VirtuosoMasonryProps<Data, Context> extends ScrollerProps {
/**
* The number of columns to be rendered. This prop is required.
* The component distributes items across columns using a shortest-column-first algorithm.
*
* @example
* ```tsx
* <VirtuosoMasonry columnCount={3} data={items} />
* ```
*/

@@ -61,15 +71,38 @@ columnCount: number;

/**
* The data to be rendered. This prop is required.
* The data array to be rendered. This prop is required.
* Each item will be passed to the {@link ItemContent} component for rendering.
*
* @example
* ```tsx
* const items = [
* { id: 1, title: 'Item 1', imageUrl: '...' },
* { id: 2, title: 'Item 2', imageUrl: '...' },
* ]
* <VirtuosoMasonry data={items} columnCount={3} />
* ```
*/
data: Data[];
/**
* Use this prop for SSR rendering.
* Use this prop for SSR rendering of a pre-defined amount of items.
*/
initialItemCount?: number;
/**
* A React component that's used to render the individual item. See {@link ItemContent} for further details on the accepted props.
* A React component that renders each individual item in the masonry grid.
* Receives `data`, `index`, and `context` as props.
*
* @example
* ```tsx
* const MyItemContent: ItemContent<MyData> = ({ data, index }) => (
* <div style={{ padding: 8 }}>
* <img src={data.imageUrl} style={{ width: '100%' }} />
* <p>{data.title}</p>
* </div>
* )
*
* <VirtuosoMasonry data={items} columnCount={3} ItemContent={MyItemContent} />
* ```
*/
ItemContent?: NoInfer<ItemContent<Data, Context>>;
/**
* Set to true to make the component use window scroll instead of the scroller element.
* Set to true to make the component use the document scroller instead of creating an element with `overflow-y: auto`.
*/

@@ -76,0 +109,0 @@ useWindowScroll?: boolean;

import { jsxs as Yt, jsx as M, Fragment as Dt } from "react/jsx-runtime";
import { Cell as g, link as vt, pipe as w, filter as St, map as y, DerivedCell as $, combine as gt, withLatestFrom as It, changeWith as $t, Signal as Rt, scan as Nt, sub as jt, pub as Gt, Realm as Ut, RealmContext as qt, useRealm as Kt, useCellValues as Jt, useCellValue as Qt } from "@virtuoso.dev/gurx";
import { forwardRef as Xt, useMemo as Zt, useImperativeHandle as te, useEffect as Ct, useState as ee, useRef as ft, useCallback as _ } from "react";
const Et = ({ index: t }) => /* @__PURE__ */ Yt("div", { children: [
import { Cell as g, link as vt, pipe as w, filter as Rt, map as y, DerivedCell as S, combine as gt, withLatestFrom as It, changeWith as St, Signal as $t, scan as Nt, sub as jt, pub as Gt, Realm as Ut, RealmContext as qt, useRealm as Kt, useCellValues as Jt, useCellValue as Qt } from "@virtuoso.dev/gurx";
import { forwardRef as Xt, useMemo as Zt, useImperativeHandle as te, useEffect as Et, useState as ee, useRef as ft, useCallback as _ } from "react";
const Ct = ({ index: t }) => /* @__PURE__ */ Yt("div", { children: [
"Item ",
t
] }), Mt = g(Et), D = g(0), at = g(null), tt = g(null, () => {
] }), Mt = g(Ct), D = g(0), at = g(null), tt = g(null, () => {
vt(
w(
tt,
St((t) => t !== null),
Rt((t) => t !== null),
y((t) => t.length)

@@ -16,3 +16,3 @@ ),

);
}), X = g(0), ht = g(0), ne = g(0), Q = g(0), re = X, et = g(0), j = g(!1), oe = $(0, () => w(
}), X = g(0), ht = g(0), ne = g(0), Q = g(0), et = g(0), j = g(!1), re = S(0, () => w(
gt(ht, j, et),

@@ -116,3 +116,3 @@ y(([t, e, n]) => e ? t - Math.max(n, 0) : t)

const r = U(t, e)[0];
return se(dt(t, r, n));
return oe(dt(t, r, n));
}

@@ -131,3 +131,3 @@ function Tt(t, e) {

}
function se(t) {
function oe(t) {
return Tt(t, ({ k: e, v: n }) => ({ index: e, value: n }));

@@ -143,3 +143,3 @@ }

}
function le(t) {
function se(t) {
const { endIndex: e, size: n, startIndex: r } = t;

@@ -157,7 +157,7 @@ return (o) => o.start === r && (o.end === e || o.end === Number.POSITIVE_INFINITY) && o.value === n;

const i = At(t, l - 1, o + 1);
if (i.some(le(r)))
if (i.some(se(r)))
continue;
let u = !1, c = !1;
for (const { end: d, start: R, value: S } of i)
u ? (o >= R || s === S) && (t = mt(t, R)) : (c = S !== s, u = !0), d > o && o >= R && S !== s && (t = k(t, o + 1, S));
for (const { end: d, start: $, value: R } of i)
u ? (o >= $ || s === R) && (t = mt(t, $)) : (c = R !== s, u = !0), d > o && o >= $ && R !== s && (t = k(t, o + 1, R));
c && (t = k(t, l, s));

@@ -185,6 +185,6 @@ }

}
function ie(t, e, n) {
function le(t, e, n) {
return t[rt(t, e, n)];
}
function ce(t, e, n, r) {
function ie(t, e, n, r) {
const o = rt(t, e, r), s = rt(t, n, r, o);

@@ -196,12 +196,12 @@ return t.slice(o, s + 1);

}
function ue({ offset: t }, e) {
function ce({ offset: t }, e) {
return e === t ? 0 : e < t ? -1 : 1;
}
function fe(t) {
function ue(t) {
return { index: t.index, value: t };
}
function ae(t, e, n, r = 0) {
return r > 0 && (e = Math.max(e, ie(t, r, Wt).offset)), e = Math.max(0, e), Tt(ce(t, e, n, ue), fe);
function fe(t, e, n, r = 0) {
return r > 0 && (e = Math.max(e, le(t, r, Wt).offset)), e = Math.max(0, e), Tt(ie(t, e, n, ce), ue);
}
function he(t, [e, n]) {
function ae(t, [e, n]) {
let r = 0, o = 0, s = 0, l = 0;

@@ -222,3 +222,3 @@ if (n !== 0) {

}
function me(t, [e, n]) {
function he(t, [e, n]) {
if (n.length > 0 && a(t) && e.length === 2) {

@@ -233,4 +233,4 @@ const r = e[0].size, o = e[1].size;

}
const zt = Rt(), Ft = Rt(), lt = g([], () => {
$t(lt, Ft, (t, e) => {
const zt = $t(), Ft = $t(), lt = g([], () => {
St(lt, Ft, (t, e) => {
const n = t.slice();

@@ -264,3 +264,3 @@ for (const [r, o] of Object.entries(e))

ot
), $t(ot, zt, (t, e) => {
), St(ot, zt, (t, e) => {
const n = t.map(([o]) => o), r = [];

@@ -273,15 +273,15 @@ for (let o = 0; o < e.length; o++) {

}
r.push(me(s, [e[o], []]));
r.push(he(s, [e[o], []]));
}
return r;
});
}), _t = $([], () => w(
}), _t = S([], () => w(
ot,
y((t) => t.map(([e]) => e))
)), de = $([], (t) => t.pipe(
)), me = S([], (t) => t.pipe(
ot,
y((e) => e.map(([, n]) => n))
)), it = $([], () => w(
)), it = S([], () => w(
_t,
It(de),
It(me),
Nt((t, [e, n]) => {

@@ -291,7 +291,7 @@ const r = [];

const s = t[o] ?? [[]];
r.push(he(s[0], [e[o], n[o]]));
r.push(ae(s[0], [e[o], n[o]]));
}
return r;
}, [])
)), xe = $(
)), de = S(
[],

@@ -302,3 +302,3 @@ () => w(

)
), ve = $(
), xe = S(
[],

@@ -309,3 +309,3 @@ () => w(

)
), ge = $(
), ve = S(
[],

@@ -316,3 +316,3 @@ () => w(

)
), Ie = $(
), ge = S(
[],

@@ -323,3 +323,3 @@ (t) => t.pipe(

)
), Y = $([], () => w(
), Y = S([], () => w(
P,

@@ -344,11 +344,11 @@ It(D, pt, lt),

})
)), pe = $(
)), Ie = S(
[],
() => w(
gt(P, D, Ie, ge, ve, Y),
gt(P, D, ge, ve, xe, Y),
y(([t, e, n, r, o, s]) => {
const l = s.reduce((c, d) => c + d.length, 0), i = Math.ceil((e - l) / t);
return Array.from({ length: t }, (c, d) => {
const S = s[d].length + i - n[d];
return r[d] + S * o[d];
const R = s[d].length + i - n[d];
return r[d] + R * o[d];
});

@@ -362,8 +362,8 @@ })

P,
X,
re,
oe,
_t,
xe,
de,
D,
pe,
Ie,
tt,

@@ -386,4 +386,4 @@ Y,

d,
$,
R,
S,
B

@@ -393,3 +393,3 @@ ]) => {

return e;
const bt = B ? S + r : 0, f = Math.min(Math.max(r - bt, 0), Math.max(...u) - o), h = f + o, b = R === 0 ? Math.min(n, i) : R;
const bt = B ? R + r : 0, f = Math.min(Math.max(r - bt, 0), Math.max(...u) - o), h = f + o, b = $ === 0 ? Math.min(n, i) : $;
return {

@@ -399,12 +399,12 @@ columns: Array.from(

(q, I) => {
const x = e.columns[I], V = s[I] ?? nt(), C = l[I] ?? [], p = u[I] ?? 0, H = d[I] ?? [];
const x = e.columns[I], V = s[I] ?? nt(), E = l[I] ?? [], p = u[I] ?? 0, H = d[I] ?? [];
if (a(V) || H.length === 0) {
const A = [];
let T = 0;
for (let E = I; E < b; E += n)
for (let C = I; C < b; C += n)
A.push({
columnIndex: I,
data: c == null ? void 0 : c[E],
data: c == null ? void 0 : c[C],
height: 0,
index: E,
index: C,
indexInColumn: T++,

@@ -418,3 +418,3 @@ offset: 0

listTop: 0,
offsetTree: C,
offsetTree: E,
totalHeight: 0,

@@ -426,13 +426,13 @@ viewportBottom: h

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
x && x.offsetTree === C && e.totalCount === i && e.data === c && e.indexesInColumns === d && f >= x.listTop && h <= x.listBottom
(x == null ? void 0 : x.offsetTree) === E && e.totalCount === i && e.data === c && e.indexesInColumns === d && f >= x.listTop && h <= x.listBottom
)
return x;
const K = [], W = H.length - 1, z = 0, ct = ae(C, f, h, z);
const K = [], W = H.length - 1, z = 0, ct = fe(E, f, h, z);
let v = 0, L = 0, O = !1;
for (const A of ct) {
const {
value: { height: T, offset: E }
value: { height: T, offset: C }
} = A;
let F = A.start;
v = E, E < f && (F += Math.floor((f - E) / T), v += (F - A.start) * T), F < z && (v += (z - F) * T, F = z);
v = C, C < f && (F += Math.floor((f - C) / T), v += (F - A.start) * T), F < z && (v += (z - F) * T, F = z);
const Bt = Math.min(A.end, W);

@@ -456,3 +456,3 @@ for (let J = F; J <= Bt && !(v >= h); J++) {

listTop: L,
offsetTree: C,
offsetTree: E,
totalHeight: p,

@@ -480,3 +480,3 @@ viewportBottom: h

st,
St((e) => e.columns.some((n) => n.listBottom < n.viewportBottom))
Rt((e) => e.columns.some((n) => n.listBottom < n.viewportBottom))
),

@@ -491,4 +491,4 @@ (e) => {

);
}), be = Xt(
({ columnCount: t, context: e, data: n, initialItemCount: r = 0, ItemContent: o = Et, useWindowScroll: s = !1, ...l }, i) => {
}), pe = Xt(
({ columnCount: t, context: e, data: n, initialItemCount: r = 0, ItemContent: o = Ct, useWindowScroll: s = !1, ...l }, i) => {
const u = Zt(() => {

@@ -505,3 +505,3 @@ const c = new Ut();

}, []);
return te(i, () => ({}), []), Ct(() => {
return te(i, () => ({}), []), Et(() => {
u.pubIn({

@@ -512,7 +512,7 @@ [P]: t,

});
}), /* @__PURE__ */ M(qt.Provider, { value: u, children: /* @__PURE__ */ M(ye, { ...l }) });
}), /* @__PURE__ */ M(qt.Provider, { value: u, children: /* @__PURE__ */ M(we, { ...l }) });
}
);
be.displayName = "VirtuosoMasonry";
const we = {
pe.displayName = "VirtuosoMasonry";
const be = {
// eslint-disable-next-line @typescript-eslint/no-empty-function

@@ -524,3 +524,3 @@ observe: () => {

}
}, ye = ({ style: t, ...e }) => {
}, we = ({ style: t, ...e }) => {
const n = Kt(), [r] = ee(() => {

@@ -530,3 +530,3 @@ if (typeof ResizeObserver > "u") {

throw new Error("ResizeObserver not found. Please ensure that you have a polyfill installed.");
return we;
return be;
}

@@ -537,3 +537,3 @@ return new ResizeObserver((f) => {

for (let V = 0; V < h; V++) {
const C = f[V], p = C.target;
const E = f[V], p = E.target;
if (p === l.current) {

@@ -545,5 +545,5 @@ const O = p.ownerDocument.defaultView;

[X]: N ? O.scrollY : p.scrollTop,
[ht]: N ? O.innerHeight : C.contentRect.height,
[ht]: N ? O.innerHeight : E.contentRect.height,
[ne]: N ? O.innerWidth : p.clientWidth,
...N ? {} : { [et]: C.contentRect.top, [Q]: p.scrollHeight }
...N ? {} : { [et]: E.contentRect.top, [Q]: p.scrollHeight }
};

@@ -561,3 +561,3 @@ continue;

continue;
const H = Number.parseInt(p.dataset.index), K = Number.parseFloat(p.dataset.knownSize ?? ""), W = C.contentRect.height, z = Number.parseInt(p.dataset.columnIndex ?? "0"), ct = Number.parseInt(p.dataset.absoluteIndex ?? "0");
const H = Number.parseInt(p.dataset.index), K = Number.parseFloat(p.dataset.knownSize ?? ""), W = E.contentRect.height, z = Number.parseInt(p.dataset.columnIndex ?? "0"), ct = Number.parseInt(p.dataset.absoluteIndex ?? "0");
if (I[ct] = W, W === K)

@@ -604,4 +604,4 @@ continue;

[r, c]
), [R, S, B] = Jt(st, Mt, j);
return Ct(() => {
), [$, R, B] = Jt(st, Mt, j);
return Et(() => {
var f;

@@ -641,3 +641,3 @@ if (B) {

},
children: R.columns.map((f, h) => /* @__PURE__ */ M(
children: $.columns.map((f, h) => /* @__PURE__ */ M(
"div",

@@ -654,3 +654,3 @@ {

},
children: f.items.map((b) => /* @__PURE__ */ M(Se, { item: b, ItemContent: S, mount: o, unmount: s }, b.index))
children: f.items.map((b) => /* @__PURE__ */ M(ye, { item: b, ItemContent: R, mount: o, unmount: s }, b.index))
},

@@ -661,3 +661,3 @@ `column-${h}`

) });
}, Se = ({ item: t, ItemContent: e, mount: n, unmount: r }) => {
}, ye = ({ item: t, ItemContent: e, mount: n, unmount: r }) => {
const o = Qt(at), s = ft(null), l = _(

@@ -689,3 +689,3 @@ (i) => {

export {
be as VirtuosoMasonry
pe as VirtuosoMasonry
};

@@ -18,6 +18,11 @@ {

"homepage": "https://virtuoso.dev/virtuoso-masonry/",
"repository": {
"type": "git",
"url": "git+https://github.com/petyosi/react-virtuoso.git",
"directory": "packages/masonry"
},
"bugs": {
"email": "support@virtuoso.dev"
},
"version": "1.3.5",
"version": "1.4.0",
"type": "module",

@@ -29,18 +34,7 @@ "module": "dist/index.js",

"publishConfig": {
"access": "public"
"access": "public",
"provenance": true
},
"scripts": {
"build": "tsc && vite build",
"dev": "vite build",
"ladle": "ladle serve",
"test": "vitest",
"e2e": "echo 'No e2e tests'",
"typecheck": "tsc --noEmit",
"docs:build": "npx typedoc --plugin typedoc-plugin-no-inherit --watch --excludeInternal --excludePrivate --out docs --name 'Virtuoso Masonry' src/index.ts",
"docs:serve": "npx serve docs",
"lint": "eslint",
"knip": "knip"
},
"dependencies": {
"@virtuoso.dev/gurx": "*"
"@virtuoso.dev/gurx": "1.2.0"
},

@@ -52,11 +46,11 @@ "peerDependencies": {

"devDependencies": {
"@virtuoso.dev/tooling": "*",
"@ladle/react": "^5.0.1",
"@microsoft/api-extractor": "^7.55.1",
"@types/node": "^22.10.1",
"@types/react": "^18.2.64",
"@types/react-dom": "^18.2.21",
"@vitejs/plugin-react-swc": "^3.7.2",
"@vitest/browser": "^3.0.4",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.7.0",
"@vitest/browser": "^4.0.16",
"@vitest/browser-playwright": "^4.0.16",
"eslint": "^9.19.0",
"globals": "^15.14.0",
"jsdom": "^26.0.0",

@@ -67,12 +61,9 @@ "knip": "^5.38.3",

"prettier": "^3.4.2",
"react": "18.3.1",
"react-dom": "18.3.1",
"resize-observer-polyfill": "^1.5.1",
"semantic-release": "^24.2.0",
"typedoc": "^0.27.2",
"typedoc-plugin-no-inherit": "^1.4.0",
"typescript": "^5.7.3",
"vite": "^6.0.11",
"vite-plugin-dts": "^4.5.0",
"vitest": "^3.0.4"
"react": "19.2.0",
"react-dom": "19.2.0",
"typescript": "~5.9.3",
"vite": "^6.4.1",
"vite-plugin-dts": "^4.5.4",
"vitest": "^4.0.16",
"@virtuoso.dev/tooling": "0.1.0"
},

@@ -82,3 +73,12 @@ "files": [

"CHANGELOG.md"
]
}
],
"scripts": {
"build": "tsc && vite build",
"dev": "vite build",
"ladle": "ladle serve",
"test": "vitest",
"typecheck": "tsgo --noEmit",
"lint": "eslint",
"knip": "knip"
}
}
+109
-1

@@ -1,1 +0,109 @@

# React Virtuoso Masonry
# Virtuoso Masonry
[![npm version](https://img.shields.io/npm/v/@virtuoso.dev/masonry.svg)](https://www.npmjs.com/package/@virtuoso.dev/masonry) [![npm downloads](https://img.shields.io/npm/dm/@virtuoso.dev/masonry.svg)](https://www.npmjs.com/package/@virtuoso.dev/masonry) [![license](https://img.shields.io/npm/l/@virtuoso.dev/masonry.svg)](https://github.com/petyosi/react-virtuoso/blob/master/LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
A virtualized masonry layout component for React that arranges items in a grid with varying heights.
## Features
- **Virtualized rendering** - only renders visible items for optimal performance with large datasets
- **Variable item heights** - items can have different heights, automatically measured and arranged
- **Dynamic column count** - change column count based on screen/container width
- **Window scroll support** - can use the window as the scroll container
- **Just-in-time distribution** - items are distributed to columns as you scroll
:::note
The column distribution algorithm distributes the items just-in-time, so if you scroll very fast, you will be able to see the arrangement happening.
:::
## Installation
```bash
npm install @virtuoso.dev/masonry
```
## Quick Start
```tsx live
import { VirtuosoMasonry } from '@virtuoso.dev/masonry'
import { useMemo } from 'react'
const ItemContent: React.FC<{ data: number }> = ({ data }) => {
const height = data % 10 === 0 ? 200 : data % 5 === 0 ? 180 : data % 7 ? 150 : 120
return (
<div style={{ padding: '5px' }}>
<div style={{ height, border: '1px solid black' }}>Item {data}</div>
</div>
)
}
export default function App() {
const data = useMemo(() => {
return Array.from({ length: 1000 }, (_, index) => index)
}, [])
return (
<div>
<VirtuosoMasonry columnCount={3} data={data} style={{ height: 500 }} initialItemCount={50} ItemContent={ItemContent} />
</div>
)
}
```
## Window Scroll Example
```tsx live
import { VirtuosoMasonry } from '@virtuoso.dev/masonry'
import { useEffect, useMemo, useState } from 'react'
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth)
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth)
window.addEventListener('resize', handleResize)
return () => window.removeEventListener('resize', handleResize)
}, [])
return width
}
const ItemContent: React.FC<{ data: number }> = ({ data }) => {
const height = data % 10 === 0 ? 200 : data % 5 === 0 ? 180 : data % 7 ? 150 : 120
return (
<div style={{ padding: '5px' }}>
<div style={{ height, border: '1px solid black' }}>Item {data}</div>
</div>
)
}
export default function App() {
const data = useMemo(() => {
return Array.from({ length: 1000 }, (_, index) => index)
}, [])
const width = useWindowWidth()
const columnCount = useMemo(() => {
if (width < 500) {
return 2
}
if (width < 800) {
return 3
}
return 4
}, [width])
return (
<div>
<VirtuosoMasonry columnCount={columnCount} data={data} useWindowScroll={true} initialItemCount={50} ItemContent={ItemContent} />
</div>
)
}
```
## Links
- [Documentation](https://virtuoso.dev/masonry/)
- [API Reference](https://virtuoso.dev/masonry/api-reference/)
- [Contributing](https://github.com/petyosi/react-virtuoso/blob/master/CONTRIBUTING.md)
- [Changelog](./CHANGELOG.md)
- [License](./LICENSE)