Efficiently rendered large list.
Only the rows in view are kept in the DOM; the scrollbar tracks the full list.
9 of 2,400 rows exist in the DOM right now.
<div class="vl" style="--accent:#D9C8F0;">
<h3 class="vl__heading">Contacts (2,400 rows)</h3>
<p class="vl__explain">Only the rows in view are kept in the DOM; the scrollbar tracks the full list.</p>
<div class="vl__viewport">
<ul class="vl__list" data-window="5">
<li style="height:34px;">Alicia Ferreira</li>
<li style="height:34px;">Ben Sato</li>
<li style="height:34px;">Charu Deshpande</li>
<li style="height:34px;">Diego Morales</li>
<li style="height:34px;">Erin Walsh</li>
<li style="height:34px;">Femi Adeyemi</li>
<li style="height:34px;">Grace Kim</li>
</ul>
<div class="vl__spacer" aria-hidden="true"></div>
</div>
<p class="vl__status" role="status">9 of 2,400 rows exist in the DOM right now.</p>
</div>
<style>
.vl { font: 14px/1.4 system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 300px; width: 300px; padding: 16px; border-radius: 12px; box-sizing: border-box; }
.vl__heading { margin: 0 0 4px; font-size: 15px; font-weight: 700; }
.vl__explain { margin: 0 0 10px; font-size: 12px; color: #241430a0; }
.vl__viewport { max-height: 176px; overflow: auto; position: relative; border: 1px solid #24143020; border-radius: 8px; }
.vl__list { list-style: none; margin: 0; padding: 0; }
.vl__list li { display: flex; align-items: center; padding: 0 10px; border-bottom: 1px solid #24143014; border-left: 3px solid var(--accent); background: #fff; }
ul[data-window="3"] li:nth-child(n+4) { display: none; }
ul[data-window="5"] li:nth-child(n+6) { display: none; }
ul[data-window="7"] li:nth-child(n+8) { display: none; }
.vl__spacer { height: 900px; background: repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 34px); }
.vl__status { margin: 10px 0 0; font-size: 12px; color: #241430a0; }
</style><div class="vl-tw font-sans text-sm text-[#241430] bg-[#FAF7F2] max-w-[300px] w-[300px] p-4 rounded-xl box-border" style="--accent:#D9C8F0">
<h3 class="m-0 mb-1 text-[15px] font-bold">Contacts (2,400 rows)</h3>
<p class="m-0 mb-2.5 text-xs text-[#241430a0]">Only the rows in view are kept in the DOM; the scrollbar tracks the full list.</p>
<div class="vl-tw__viewport max-h-[176px] overflow-auto relative border border-[#24143020] rounded-lg">
<ul class="vl-tw__list list-none m-0 p-0" data-window="5">
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Alicia Ferreira</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Ben Sato</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Charu Deshpande</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Diego Morales</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Erin Walsh</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Femi Adeyemi</li>
<li class="flex items-center px-2.5 border-b border-[#24143014] bg-white" style="height:34px;border-left:3px solid #D9C8F0">Grace Kim</li>
</ul>
<div aria-hidden="true" class="h-[900px]" style="background:repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 34px);"></div>
</div>
<p role="status" class="mt-2.5 mb-0 text-xs text-[#241430a0]">9 of 2,400 rows exist in the DOM right now.</p>
</div>
<style>
ul[data-window="3"] li:nth-child(n+4) { display: none; }
ul[data-window="5"] li:nth-child(n+6) { display: none; }
ul[data-window="7"] li:nth-child(n+8) { display: none; }
</style>export function VirtualList({
heading = "Contacts (2,400 rows)",
accent = "#D9C8F0",
windowSize = "5",
rowHeight = 34,
rendered = 9,
}) {
const names = ["Alicia Ferreira", "Ben Sato", "Charu Deshpande", "Diego Morales", "Erin Walsh", "Femi Adeyemi", "Grace Kim"].slice(0, Number(windowSize));
return (
<div style={{ fontFamily: "system-ui, sans-serif", fontSize: 14, color: "#241430", background: "#FAF7F2", maxWidth: 300, width: 300, padding: 16, borderRadius: 12, boxSizing: "border-box" }}>
<h3 style={{ margin: "0 0 4px", fontSize: 15, fontWeight: 700 }}>{heading}</h3>
<p style={{ margin: "0 0 10px", fontSize: 12, color: "#241430a0" }}>Only the rows in view are kept in the DOM; the scrollbar tracks the full list.</p>
<div style={{ maxHeight: 176, overflow: "auto", position: "relative", border: "1px solid #24143020", borderRadius: 8 }}>
<ul style={{ listStyle: "none", margin: 0, padding: 0 }}>
{names.map((name) => (
<li key={name} style={{ display: "flex", alignItems: "center", height: rowHeight, padding: "0 10px", borderBottom: "1px solid #24143014", borderLeft: `3px solid ${accent}`, background: "#fff" }}>{name}</li>
))}
</ul>
<div aria-hidden="true" style={{ height: 900, background: "repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 34px)" }} />
</div>
<p role="status" style={{ margin: "10px 0 0", fontSize: 12, color: "#241430a0" }}>{rendered} of 2,400 rows exist in the DOM right now.</p>
</div>
);
}