<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Window 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; }
p { color: #555; margin-bottom: 16px; max-width: 70ch; }
code { background: #f3f4f6; padding: 1px 5px; border-radius: 4px; font-size: 12px; }
.item { display: flex; align-items: center; padding: 0 12px; font-size: 13px; border-bottom: 1px solid #f3f4f6; box-sizing: border-box; }
.item-even { background: #f9fafb; }
.item-odd { background: #ffffff; }
</style>
</head>
<body>
<h1>Window Virtualisation</h1>
<p>
Uses <window-virtualizer> — the entire page scrolls. The list does not start at the
top of the page (this heading sits above it), so its offset from the document top is
measured on mount and passed as <code>scrollMargin</code>. The virtualizer then computes
visibility relative to the list itself; <code>item.start</code> values include the margin,
so each row subtracts it when positioning inside the wrapper. Scroll down to see rows.
</p>
// The list's distance from the top of the document. 0 during SSR and first paint;
// measured from the real layout on mount. Passing it as `scrollMargin` shifts the
// virtualizer's position math so the window of rendered rows tracks the LIST's
// visibility, not the page top's.
<let/listOffset=0/>
<script>
const el = listEl()
if (el) listOffset = (el as unknown as HTMLElement).offsetTop
</script>
<window-virtualizer/v
count=10000
estimateSize=() => 35
overscan=5
scrollMargin=listOffset
/>
// totalSize is already margin-free (core subtracts scrollMargin), so it is the
// wrapper's height as-is; only item.start values carry the margin.
<div/listEl style=`height: ${v.totalSize}px; width: 100%; position: relative`>
<for|item| of=v.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 - listOffset}px)`
>
Row ${item.index}
</div>
</for>
</div>
</body>
</html>