Virtual Feeds
Build large Masonix feeds with range events, end-reached loading, and scroll seek.
MasonryVirtual keeps large feeds responsive by rendering only the items near
the viewport. The layout is still computed for the full list, so scroll height,
item positions, and imperative jumps remain consistent.
Activity feed
Loaded 18 of 48
Infinite loading
Use onEndReached when the rendered range reaches the loaded end.
<MasonryVirtual
items={items}
totalItems={totalCount}
columns={{ 0: 1, 600: 2 }}
gap={12}
estimatedItemHeight={280}
endReachedThreshold={4}
onEndReached={() => loadMore()}
render={({ data }) => <FeedCard item={data} />}
/>onEndReached fires once per loaded item count, so small scroll movements near
the end do not repeatedly call your loader for the same data.
Range changes
Use onRangeChange when you need the current rendered range.
<MasonryVirtual
items={items}
onRangeChange={(startIndex, stopIndex) => {
reportVisibleRange({ startIndex, stopIndex });
}}
render={({ data }) => <FeedCard item={data} />}
/>The range includes overscan. It is useful for diagnostics, analytics, or prefetching.
Scroll seek
scrollSeek swaps real card rendering for lightweight placeholders while
scroll velocity is above the threshold. Placeholders receive the same width and
height budget, so the layout does not collapse.
Restore an initial position
Use initialScrollIndex to restore a feed without waiting for a ref effect.
<MasonryVirtual
items={items}
initialScrollIndex={{ index: savedIndex, align: 'auto' }}
render={({ data }) => <FeedCard item={data} />}
/>The initial jump runs once after the requested item is loaded. Use
scrollRef.current.scrollToIndex() for navigation after mount.
Practical notes
- Use realistic
estimatedItemHeightvalues. - Add
getItemHeightwhen item heights are already known. - Pass
scrollContainerwhen the feed scrolls inside a panel instead of the window. - Keep card rendering pure and avoid expensive effects inside visible items.