MATES / Docs

Performance

Why Mates stays fast and how to keep it that way — targeted DOM patches, keyed lists, memoization, and virtualization.

Why it's fast by default

Mates does not walk a virtual DOM. Templates track which atoms they read and patch exactly those bindings. The outer component function runs once, so per-update cost is the template expression itself — not a full component re-execution.

  • No virtual DOM diffing — a toggle updates one text node, not a tree walk
  • Two-layer components — setup once; render is cheap and targeted
  • No compiler pass — nothing to mis-optimize; what you write is what runs

Numbers live on the Benchmarks page — keyed list ops vs React, Solid, Vue Vapor, and Angular (Mates plus Mates raw components), plus a 100k-todo full-DOM paint test vs Solid, Vue 3 (not Vapor), Angular 19.2, and React.

Key your lists

repeat() with a key function is the single biggest lever on large lists. Keyed rows are reused and patched in place; unkeyed renders rebuild DOM nodes:

import { html, repeat } from "mates";

return () => html`
  <ul>
    ${repeat(
      todos(),
      (todo) => todo.id,
      (todo) => html`<li>${todo.title}</li>`,
    )}
  </ul>
`;

Derive, don't recompute

Any value computed from other state belongs in memo() or a store getter — it recomputes only when its inputs change and stays out of the render path:

import { atom, memo, html } from "mates";

const todos = atom<Todo[]>([]);
const openCount = memo(() => todos().filter((t) => !t.done).length);

return () => html`<span>${openCount()} open</span>`;
  • Do heavy work in the outer function or a memo — never inside the render function
  • Read atoms once per template where possible; reading the same atom in ten bindings is ten subscriptions (cheap, but avoidable)
  • Split big components so a state change re-renders a subtree, not the page
  • Prefer derivation over effect that copies atom → atom — Patterns

Virtualize very large lists

Beyond a few thousand rows, don't render what you can't see. mates-virtual provides directives for lists, tables, masonry, and grids:

npm install mates-virtual
import { html } from "mates";
import { virtualList } from "mates-virtual";

return () => html`
  ${virtualList({
    items: todos(),
    renderItem: (todo) => html`<li>${todo.title}</li>`,
  })}
`;

Exports: virtualList, virtualTable, virtualMasonry, masonryGrid. Package overview: Ecosystem.

Bundle size

  • The full mates package is ~44KB gzipped including router, fetch, dates, CSS-in-JS, and WebSockets — one install, no per-feature packages to wire
  • Optional packages (mates-virtual, mates-db, mates-ui) stay out of the bundle until you import them
  • mates-icons exports icons one-per-module — tree-shaking keeps unused icons out of production builds