Resize Relayout Playground
Drag a width slider and watch dozens of text blocks relayout in real time.
This demonstrates the performance advantage of Pretext's prepare-once model. All text blocks are prepared once, then relayouted cheaply as you drag the width slider. In a traditional DOM approach, each block would trigger a separate reflow.
prepare()layout() What this demonstrates
Batch relayout of many text blocks at interactive frame rates. Each block was prepared once;
changing the container width only calls layout() — the fast path. This pattern
applies to resizable panels, split panes, and any adaptive container.
Relevant Pretext API
prepare(text, font)— called once per blocklayout(prepared, maxWidth, lineHeight)— called on every resize
Why this is hard with the DOM
With DOM-based measurement, resizing N text blocks requires N reflows. Each reflow forces the browser to recalculate layout for the entire page. As N grows, this becomes a performance cliff. Pretext makes the per-block cost negligible.
Quick start
import { prepare, layout } from '@chenglou/pretext';
// 16 text blocks, each with different content.
// prepare() is called ONCE per block — the expensive step.
const font = '14px Inter, sans-serif';
const lineHeight = 21; // fontSize * 1.5
const preparedBlocks = texts.map(text => ({
text,
prepared: prepare(text, font), // ~0.5–1ms each
result: null as { height: number; lineCount: number } | null,
}));
// Batch relayout: runs layout() on ALL 16 blocks at a new width.
// This is the hot path that runs on every resize frame.
function relayoutAll(newWidth: number) {
const t0 = performance.now();
for (const block of preparedBlocks) {
block.result = layout(block.prepared, newWidth, lineHeight);
}
const totalUs = Math.round((performance.now() - t0) * 1000);
const perBlockUs = Math.round(totalUs / preparedBlocks.length);
// Typical results: ~15μs total, ~1μs per block.
// That's 16 text measurements in less than 0.02ms.
console.log(`${preparedBlocks.length} blocks: ${totalUs}μs total, ${perBlockUs}μs each`);
return preparedBlocks;
}
// Initial layout at 400px
relayoutAll(400);
// On resize — relayout is pure arithmetic, no DOM reads.
// Compare: DOM approach would trigger 16 reflows per frame.
let animFrame: number;
function onResize() {
cancelAnimationFrame(animFrame);
animFrame = requestAnimationFrame(() => {
const blocks = relayoutAll(container.offsetWidth);
blocks.forEach((b, i) => {
// Use computed heights to position elements
elements[i].style.height = b.result!.height + 'px';
});
});
}
window.addEventListener('resize', onResize);