Perf bench
The last lesson is a tool. Two panels, same job: measure workload items of public-domain prose against a 400-pixel column. The left panel reads heights off the DOM. The right panel asks Pretext. Each panel times itself.
Crank the workload. Run one pane, then the other. At workload 500 both finish in a blink. At workload 5000 the numbers separate. Use this to answer a single decision: for your use case, does Pretext earn its seat?
getBoundingClientRect() per item · forces layout flushlayout() is pure arithmeticMechanism
Both panels walk the same pool of 1,000 public-domain paragraphs and measure the rendered height of each at a fixed column width. The inputs are identical; only the engine differs.
DOM panel: for each item we push text into an off-screen probe, set the probe's width, and read getBoundingClientRect().height. That read forces the browser to flush any pending style/layout work. Doing it N times gives us a measured height N times — and N forced flushes.
Pretext panel: at mount we call prepare() once on each item and cache the handle. Each run iterates the cached handles and calls layout(handle, 400, lineHeight). No DOM touch, no flush, no canvas call. The work is word-wrap arithmetic over cached segment widths.
At low workload both engines are fast enough that the difference doesn't matter. At high workload the DOM panel's time scales with N layout flushes; the Pretext panel's time stays a handful of milliseconds. Your user's slider handler is a high-workload environment in disguise.
Application
This bench is the decision moment. If your component's measurement budget per frame is 16ms and you need to measure 500 items during a drag — is the DOM panel's rate enough, or do you need the Pretext panel's? Run it.
Use it before you adopt the library: confirm the gap is real on your machine. Use it after you adopt: sanity-check a regression by running the Pretext pane and watching the rate stay where it was. Use it when a teammate asks whether it's worth the dependency: run both, show the number.
The shape of the answer does not depend on marketing. It depends on your browser, your machine, your text, and the engine you picked.
"I learned this, at least, by my experiment: that if one advances confidently in the direction of his dreams, and endeavors to live the life which he has imagined, he will meet with a success unexpected in common hours."
Henry David Thoreau, Walden (1854, conclusion)Direct Claude
The course ends here. The primitives are small; the applications are not.
import { prepare, layout } from '@chenglou/pretext';
// Mount: prepare once per item. The handles are shared across runs.
const handles = items.map(t => prepare(t, FONT));
// Pretext pane: N layout() calls. Pure arithmetic.
function runPretext(n) {
const t0 = performance.now();
for (let i = 0; i < n; i++) {
void layout(handles[i % handles.length], 400, LINE_H).height;
}
const ms = performance.now() - t0;
return { ms, rate: n / (ms / 1000) };
}
// DOM pane: N probe reads. Each read flushes layout.
function runDOM(n) {
const t0 = performance.now();
for (let i = 0; i < n; i++) {
probe.textContent = items[i % items.length];
probe.style.width = '400px';
void probe.getBoundingClientRect().height; // forces layout
}
const ms = performance.now() - t0;
return { ms, rate: n / (ms / 1000) };
}