Mesa guide / Advanced
Virtual lists
You need this page when a list has thousands of rows and rendering them all is
slow. {#virtual each} mounts only the rows in view and pads the rest with
spacers so the scrollbar stays honest.
<script>
let rows = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: 'Row ' + i,
value: i * 3,
}))
</script>
{#virtual each rows as row (row.id) height=48 viewport="500px"}
<div class="row">
<span>{row.name}</span>
<span>{row.value}</span>
</div>
{/virtual}
<style>
.row { display: flex; justify-content: space-between; height: 48px; }
</style>
Options
Options go last, after the optional (key):
height=Nis the height of one row in pixels. Every row must be that tall. Leave it off and Mesa measures the first rendered row. Declare it when the row height comes from a stylesheet, because the first measurement happens before the stylesheet lands.viewport="500px"sets the height of the element the block sits in, which becomes the scroller. Leave it off and that element must already have a height andoverflow-y.
An option Mesa does not know is a compile error that lists the two it does.
How it works
The rows and two spacer divs are appended inside the element the block sits
in. On scroll, a requestAnimationFrame handler works out which rows should be
visible, disposes the ones that scrolled out and mounts the new ones. Each row is
a full reactive block, so signals, bind:value and child components work inside
it. A row that scrolls out loses its local state when it is disposed.
What bites
- Fixed height only. Rows of varying height are not supported. A row taller
or shorter than
heightmakes the scroll position drift. - The block's parent is the scroller. Put the block in its own element so the spacers do not mix with other children.
- On the server it renders a window, not nothing. With no viewport to measure, the first window comes from the declared (or fallback) height, and the bottom spacer carries the rest, so a prerendered page has its first screen of rows.
Spec: VISION.md § 9.7 · RULE 34