Skip to main content

Virtualised Table — Self-scrolling

mates-virtual virtualList with scroller: true. The virtualizer host IS the scroll container — no wrapper div with overflow:auto needed. The parent only sets a height. 10 000 rows, sortable columns, only the visible rows are in the DOM.

Quick start
import { virtualList } from "mates-virtual";

// scroller: true  →  the host <div> itself scrolls.
// No parent wrapper with overflow:auto needed — just set a height.
html`
  <div style="height:500px">
    ${virtualList(rows, (c) => c.id, (c) => html`…row…`, { scroller: true })}
  </div>
`

10 000 rows — scroller: true, no overflow container

import { html, atom, renderApp } from 'mates';
import { virtualList } from 'mates-virtual';
import { Avatar } from 'mates-ui';

const ROWS = Array.from({ length: 500 }, (_, i) => ({
id: i,
name: "User " + (i + 1),
initials: String.fromCharCode(65 + (i % 26)) + String.fromCharCode(65 + ((i * 3) % 26)),
role: (["Admin", "Editor", "Viewer", "Developer"])[i % 4],
dept: (["Engineering", "Design", "Marketing", "Sales"])[i % 4],
score: (i * 37 + 13) % 100,
}));

const App = () => {
const rows = atom(ROWS);
return () => html`
<div style="border:0.09rem solid var(--md-color-border);border-radius:var(--md-radius-sm);overflow:hidden;max-width:40rem;width:100%;">
<div style="display:grid;grid-template-columns:2fr 1fr 1.2fr 72px;background:var(--md-color-surface-raised);border-bottom:0.09rem solid var(--md-color-border);">
${["Name", "Role", "Department", "Score"].map((h) => html`
<div style="padding:var(--md-space-3) var(--md-space-4);font-size:var(--md-text-xs);font-weight:600;color:var(--md-color-text-muted);text-transform:uppercase;letter-spacing:.05em;">${h}</div>
`)}
</div>
<div style="height:400px;">
${virtualList(rows(), (c) => c.id, (c) => html`
<div style="display:grid;grid-template-columns:2fr 1fr 1.2fr 72px;border-bottom:0.06rem solid var(--md-color-border);">
<div style="padding:var(--md-space-3) var(--md-space-4);display:flex;align-items:center;gap:var(--md-space-2);">
${Avatar({ initials: c.initials, size: "sm" })}
<span style="font-size:var(--md-text-sm);">${c.name}</span>
</div>
<div style="padding:var(--md-space-3) var(--md-space-4);font-size:var(--md-text-sm);color:var(--md-color-text-muted);display:flex;align-items:center;">${c.role}</div>
<div style="padding:var(--md-space-3) var(--md-space-4);font-size:var(--md-text-sm);color:var(--md-color-text-muted);display:flex;align-items:center;">${c.dept}</div>
<div style="padding:var(--md-space-3) var(--md-space-4);display:flex;align-items:center;">
<span style="font-size:var(--md-text-sm);font-weight:600;color:${c.score >= 70 ? 'var(--md-color-success)' : c.score >= 40 ? 'var(--md-color-warning)' : 'var(--md-color-error)'};">${c.score}</span>
</div>
</div>
`, { scroller: true })}