Skip to content

Instantly share code, notes, and snippets.

@nelsoneldoro
Created August 20, 2025 02:20
Show Gist options
  • Save nelsoneldoro/7deeaa842c442cc25e62ef17cd697e5b to your computer and use it in GitHub Desktop.
Save nelsoneldoro/7deeaa842c442cc25e62ef17cd697e5b to your computer and use it in GitHub Desktop.
React vanilla virtualization
import { useState } from "react";
const TOTAL = 10000; // number of items
const ITEM_HEIGHT = 30; // fixed height of each item
const VIEWPORT_HEIGHT = 300; // height of the visible container
const BUFFER = 5; // extra safety buffer (before/after)
export default function VirtualizedList() {
const [scrollTop, setScrollTop] = useState(0);
// how many items fit on the screen
const visibleCount = Math.ceil(VIEWPORT_HEIGHT / ITEM_HEIGHT);
// initial and final index with buffer
const startIndex = Math.max(0, Math.floor(scrollTop / ITEM_HEIGHT) - BUFFER);
const endIndex = Math.min(TOTAL - 1, startIndex + visibleCount + BUFFER * 2);
// slice of items to render
const items = [];
for (let i = startIndex; i <= endIndex; i++) {
items.push(
<div
key={i}
style={{
height: ITEM_HEIGHT,
lineHeight: `${ITEM_HEIGHT}px`,
borderBottom: "1px solid #ddd",
}}
>
Item {i + 1}
</div>
);
}
return (
<div>
<div
style={{
height: VIEWPORT_HEIGHT,
overflowY: "auto",
border: "1px solid black",
position: "relative",
}}
onScroll={(e) => setScrollTop((e.target as HTMLDivElement).scrollTop)}
>
<div
style={{
height: TOTAL * ITEM_HEIGHT, // total virtual height
position: "relative",
}}
>
<div
data-index={startIndex}
data-end-index={endIndex}
data-scroll-top={scrollTop}
data-visible-count={visibleCount}
data-buffer={BUFFER}
style={{
transform: `translateY(${startIndex * ITEM_HEIGHT}px)`,
}}
>
{items}
</div>
</div>
</div>
<br />
<samp>
<div>TOTAL: {TOTAL}</div>
<div>ITEM_HEIGHT: {ITEM_HEIGHT}</div>
<div>VIEWPORT_HEIGHT: {VIEWPORT_HEIGHT}</div>
<div>BUFFER: {BUFFER}</div>
<div>startIndex: {startIndex}</div>
<div>endIndex: {endIndex}</div>
<div>scrollTop: {scrollTop}</div>
<div>visibleCount: {visibleCount} </div>
</samp>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment