MasonryVirtual
Virtualized masonry for large feeds, infinite loading, and fast scrolling.
MasonryVirtual computes masonry placement while rendering only the items near
the viewport. It lives in masonix/virtual so virtualization code is included
only when you import it.
When to use
- Feeds with hundreds or thousands of items.
- App panels where scroll performance matters.
- Layouts that need infinite loading or imperative jump-to-item controls.
Basic usage
import { useRef } from 'react';
import { MasonryVirtual } from 'masonix/virtual';
export function Feed({ items }: { items: FeedItem[] }) {
const scrollContainerRef = useRef<HTMLDivElement>(null);
return (
<div
ref={scrollContainerRef}
className="h-96 min-w-0 overflow-x-hidden overflow-y-auto overscroll-contain rounded-lg border p-3"
>
<MasonryVirtual
items={items}
scrollContainer={scrollContainerRef}
columns={{ 0: 1, 620: 2 }}
gap={12}
estimatedItemHeight={220}
itemKey={(item) => item.id}
render={({ data }) => <FeedCard item={data} />}
/>
</div>
);
}Infinite loading
<MasonryVirtual
items={items}
totalItems={totalCount}
columns={{ 0: 1, 600: 2 }}
gap={12}
estimatedItemHeight={220}
endReachedThreshold={4}
onEndReached={() => loadMore()}
render={({ data }) => <FeedCard item={data} />}
/>onEndReached fires when the rendered range reaches the threshold, and it does
not repeatedly fire for the same loaded item count.
Imperative scrolling
import { useRef } from 'react';
import type { MasonryVirtualHandle } from 'masonix/virtual';
const scrollRef = useRef<MasonryVirtualHandle>(null);
scrollRef.current?.scrollToIndex(42, {
align: 'center',
smooth: true,
});
scrollRef.current?.scrollBy(320, { smooth: true });
scrollRef.current?.scrollToOffset(0, { smooth: true });Pass the ref with scrollRef={scrollRef}. Use align: 'auto' to leave an
already visible item in place and otherwise reveal it with the smallest useful
scroll.
Initial position
<MasonryVirtual
items={items}
initialScrollIndex={{ index: 42, align: 'center' }}
render={({ data }) => <FeedCard item={data} />}
/>initialScrollIndex accepts an index or an index-and-alignment object. It is
applied once, without animation, after the viewport and requested item are
available. This lets an asynchronously loaded first batch restore a saved
location without turning later prop updates into unexpected jumps.
Common mistakes
- Do not forget
estimatedItemHeight; virtual layouts need an offscreen estimate. - Do not use window scrolling assumptions when the feed lives in a panel; pass
scrollContainer. - Do not render expensive cards during fast scrolling if a scroll-seek placeholder would work.