Created
August 20, 2025 02:20
-
-
Save nelsoneldoro/7deeaa842c442cc25e62ef17cd697e5b to your computer and use it in GitHub Desktop.
React vanilla virtualization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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