Virtualisation

Renders only visible records for performance.

240-row dataset
RowCustomerStatusTotal
#0001A. WhitfieldShipped£64.00
#0002R. OkaforProcessing£128.50
#0003M. IqbalShipped£22.00
#0004S. NovakRefunded£40.00
#0005L. DuarteShipped£96.20
#0006P. AnderssonProcessing£15.75
#0007K. FischerShipped£58.40
12 of 240 rows rendered in the DOM

html

<div class="virt" style="--accent:#241430;">
  <div class="virt__viewport">
    <table class="virt__table">
      <caption>240-row dataset</caption>
      <thead><tr><th scope="col">Row</th><th scope="col">Customer</th><th scope="col">Status</th><th scope="col">Total</th></tr></thead>
      <tbody data-rows="5">
        <tr style="height:36px;"><td>#0001</td><td>A. Whitfield</td><td>Shipped</td><td>£64.00</td></tr>
        <tr style="height:36px;"><td>#0002</td><td>R. Okafor</td><td>Processing</td><td>£128.50</td></tr>
        <tr style="height:36px;"><td>#0003</td><td>M. Iqbal</td><td>Shipped</td><td>£22.00</td></tr>
        <tr style="height:36px;"><td>#0004</td><td>S. Novak</td><td>Refunded</td><td>£40.00</td></tr>
        <tr style="height:36px;"><td>#0005</td><td>L. Duarte</td><td>Shipped</td><td>£96.20</td></tr>
        <tr style="height:36px;"><td>#0006</td><td>P. Andersson</td><td>Processing</td><td>£15.75</td></tr>
        <tr style="height:36px;"><td>#0007</td><td>K. Fischer</td><td>Shipped</td><td>£58.40</td></tr>
      </tbody>
    </table>
    <div class="virt__spacer" aria-hidden="true"></div>
  </div>
  <div class="virt__status" role="status">12 of 240 rows rendered in the DOM</div>
</div>
<style>
.virt { font: 14px system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 480px; border: 1px solid #24143020; border-radius: 10px; overflow: hidden; }
.virt__viewport { max-height: 210px; overflow: auto; position: relative; }
.virt__table { border-collapse: collapse; width: 100%; }
.virt__table caption { text-align: left; font-weight: 600; padding: 10px 14px; position: sticky; left: 0; }
.virt__table th, .virt__table td { padding: 0 14px; text-align: left; border-bottom: 1px solid #24143020; white-space: nowrap; }
.virt__table th { background: var(--accent); color: #FAF7F2; padding: 10px 14px; position: sticky; top: 0; }
tbody[data-rows="3"] tr:nth-child(n+4) { display: none; }
tbody[data-rows="5"] tr:nth-child(n+6) { display: none; }
tbody[data-rows="7"] tr:nth-child(n+8) { display: none; }
.virt__spacer { height: 1400px; background: repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 36px); }
.virt__status { font-size: 12px; color: #241430a0; padding: 8px 14px; border-top: 1px solid #24143020; }
</style>

tailwind

<div class="max-w-[480px] rounded-[10px] border border-[#24143020] font-sans text-sm text-[#241430] bg-[#FAF7F2] overflow-hidden" style="--accent:#241430;">
  <div class="max-h-[210px] overflow-auto relative">
    <table class="border-collapse w-full">
      <caption class="text-left font-semibold px-3.5 py-2.5 sticky left-0">240-row dataset</caption>
      <thead><tr class="text-[#FAF7F2]" style="background:#241430">
        <th scope="col" class="p-3.5 text-left whitespace-nowrap sticky top-0" style="background:#241430">Row</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap sticky top-0" style="background:#241430">Customer</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap sticky top-0" style="background:#241430">Status</th>
        <th scope="col" class="p-3.5 text-left whitespace-nowrap sticky top-0" style="background:#241430">Total</th>
      </tr></thead>
      <tbody data-rows="5">
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0001</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">A. Whitfield</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£64.00</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0002</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">R. Okafor</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Processing</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£128.50</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0003</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">M. Iqbal</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£22.00</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0004</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">S. Novak</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Refunded</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£40.00</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0005</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">L. Duarte</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£96.20</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0006</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">P. Andersson</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Processing</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£15.75</td></tr>
        <tr style="height:36px"><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">#0007</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">K. Fischer</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">Shipped</td><td class="px-3.5 border-b border-[#24143020] whitespace-nowrap">£58.40</td></tr>
      </tbody>
    </table>
    <div aria-hidden="true" class="h-[1400px]" style="background:repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 36px);"></div>
  </div>
  <div role="status" class="text-xs text-[#241430a0] px-3.5 py-2 border-t border-[#24143020]">12 of 240 rows rendered in the DOM</div>
</div>
<style>
tbody[data-rows="3"] tr:nth-child(n+4) { display: none; }
tbody[data-rows="5"] tr:nth-child(n+6) { display: none; }
tbody[data-rows="7"] tr:nth-child(n+8) { display: none; }
</style>

react

function Virtualisation({
  caption = "240-row dataset",
  accent = "#241430",
  visibleRows = "5",
  itemHeight = 36,
  renderedCount = 12,
}) {
  const rows = [
    ["#0001", "A. Whitfield", "Shipped", "£64.00"],
    ["#0002", "R. Okafor", "Processing", "£128.50"],
    ["#0003", "M. Iqbal", "Shipped", "£22.00"],
    ["#0004", "S. Novak", "Refunded", "£40.00"],
    ["#0005", "L. Duarte", "Shipped", "£96.20"],
    ["#0006", "P. Andersson", "Processing", "£15.75"],
    ["#0007", "K. Fischer", "Shipped", "£58.40"],
  ].slice(0, Number(visibleRows));
  const cell = { padding: "0 14px", borderBottom: "1px solid #24143020", whiteSpace: "nowrap" };
  return (
    <div style={{ maxWidth: 480, borderRadius: 10, border: "1px solid #24143020", overflow: "hidden", font: "14px system-ui, sans-serif", color: "#241430", background: "#FAF7F2" }}>
      <div style={{ maxHeight: 210, overflow: "auto", position: "relative" }}>
        <table style={{ borderCollapse: "collapse", width: "100%" }}>
          <caption style={{ textAlign: "left", fontWeight: 600, padding: "10px 14px" }}>{caption}</caption>
          <thead>
            <tr style={{ background: accent, color: "#FAF7F2" }}>
              {["Row", "Customer", "Status", "Total"].map((h) => <th key={h} scope="col" style={{ padding: "10px 14px", textAlign: "left", position: "sticky", top: 0, background: accent }}>{h}</th>)}
            </tr>
          </thead>
          <tbody>
            {rows.map((row) => (
              <tr key={row[0]} style={{ height: itemHeight }}>{row.map((c, j) => <td key={j} style={cell}>{c}</td>)}</tr>
            ))}
          </tbody>
        </table>
        <div aria-hidden="true" style={{ height: 1400, background: "repeating-linear-gradient(180deg, #24143008 0 1px, transparent 1px 36px)" }} />
      </div>
      <div role="status" style={{ fontSize: 12, color: "#241430a0", padding: "8px 14px", borderTop: "1px solid #24143020" }}>{renderedCount} of 240 rows rendered in the DOM</div>
    </div>
  );
}