Skip to main content

Virtualised Table — In a Container

mates-virtual virtualList inside a parent div with overflow:auto and a fixed height. The parent is the scroll container — no scroller: true needed. Use this pattern when embedding a table inside a card, panel, or layout area. 10 000 rows with sortable columns.

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

// No scroller: true  →  the parent div with overflow:auto owns the scroll.
html`
  <div style="height:400px;overflow:auto;border:1px solid …">
    ${virtualList(rows, (c) => c.id, (c) => html`…row…`)}
  </div>
`

10 000 rows inside a bordered panel — parent div scrolls

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;overflow:auto;">
${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>
`)}