TanStack

Marko Example: Variable

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Variable Virtualisation — @tanstack/marko-virtual</title>
    <style>
      * { box-sizing: border-box; margin: 0; padding: 0; }
      body { font-family: system-ui, sans-serif; padding: 24px; }
      h1 { font-size: 24px; margin-bottom: 8px; }
      h2 { font-size: 18px; margin: 24px 0 12px; }
      p { color: #555; margin-bottom: 16px; }
      section { margin-bottom: 32px; }
      .scroll-row { height: 200px; overflow-y: auto; border: 1px solid #e5e7eb; border-radius: 6px; position: relative; }
      .scroll-col { width: 400px; height: 80px; overflow-x: auto; border: 1px solid #e5e7eb; border-radius: 6px; position: relative; }
      .scroll-masonry { height: 300px; overflow-y: auto; border: 1px solid #e5e7eb; border-radius: 6px; position: relative; }
      .item { display: flex; align-items: center; padding: 0 12px; font-size: 12px; border-bottom: 1px solid #f3f4f6; box-sizing: border-box; }
      .item-even { background: #f9fafb; }
      .item-odd  { background: #ffffff; }
    </style>
  </head>
  <body>
    <h1>Variable Virtualisation</h1>
    <p>
      Each item has a variable but knowable size — passed directly via
      <code>estimateSize</code> using a deterministic formula so no DOM measurement is needed.
    </p>

    <section>
      <h2>Rows</h2>
      <div/rowScroll class="scroll-row">
        <virtualizer/rowV
          count=10000
          estimateSize=(i) => 25 + ((i * 17 + 31) % 100)
          getScrollElement=() => rowScroll()
        />
        <div style=`height: ${rowV.totalSize}px; width: 100%; position: relative`>
          <for|item| of=rowV.virtualItems>
            <div
              class=item.index % 2 === 0 ? "item item-even" : "item item-odd"
              style=`position: absolute; top: 0; left: 0; width: 100%; height: ${item.size}px; transform: translateY(${item.start}px)`
            >
              Row ${item.index} — ${item.size}px
            </div>
          </for>
        </div>
      </div>
    </section>

    <section>
      <h2>Columns</h2>
      <div/colScroll class="scroll-col">
        <virtualizer/colV
          count=10000
          estimateSize=(i) => 75 + ((i * 13 + 43) % 100)
          horizontal=true
          getScrollElement=() => colScroll()
        />
        <div style=`width: ${colV.totalSize}px; height: 100%; position: relative`>
          <for|item| of=colV.virtualItems>
            <div
              class=item.index % 2 === 0 ? "item item-even" : "item item-odd"
              style=`position: absolute; top: 0; left: 0; height: 100%; width: ${item.size}px; transform: translateX(${item.start}px)`
            >
              Col ${item.index}
            </div>
          </for>
        </div>
      </div>
    </section>

    <section>
      <h2>Masonry (vertical, 4 lanes)</h2>
      <div/masonryScroll class="scroll-masonry">
        <virtualizer/masonryV
          count=10000
          estimateSize=(i) => 25 + ((i * 17 + 31) % 100)
          lanes=4
          getScrollElement=() => masonryScroll()
        />
        <div style=`height: ${masonryV.totalSize}px; width: 100%; position: relative`>
          <for|item| of=masonryV.virtualItems>
            <div
              class=item.index % 2 === 0 ? "item item-even" : "item item-odd"
              style=`position: absolute; top: 0; left: ${item.lane * 25}%; width: 25%; height: ${item.size}px; transform: translateY(${item.start}px); box-sizing: border-box`
            >
              ${item.index}
            </div>
          </for>
        </div>
      </div>
    </section>
  </body>
</html>