For most of the web's history, "measuring text" has meant one thing: stick a <span> in the document, read its getBoundingClientRect(), and let the browser do the layout work. It works — but it forces a layout reflow every time you ask, and that reflow is the single most expensive operation in client-side rendering. Pretext, created by Cheng Lou (React core alumnus, react-motion author, ReScript and Midjourney contributor), takes a completely different path: it computes text layout as pure arithmetic, without ever touching the DOM.
The result is a typography engine you can run on every animation frame.
Pretext separates text layout into two phases:
(text, font) pair, using the browser's actual font engine via an off-screen canvas's measureText(). This produces a small numeric "fingerprint" of the text: glyph widths, break opportunities, segment boundaries.The numbers are striking. For 500 text blocks:
| Approach | Time | DOM Operations |
|---|---|---|
getBoundingClientRect() per block |
~19ms | 500 reflows |
Pretext layout() per block |
~0.09ms | 0 |
That's roughly 200x–500x faster depending on hardware and scenario — not because the code is cleverer, but because an entire class of expensive operations has been removed from the hot path.
The engine isn't an academic exercise. It targets concrete pain points that show up in real production codebases.
Virtual scrollers — the kind used in chat clients, log viewers, infinite feeds — all share one weakness: variable-height rows. To position row N you need to know how tall rows 0..N-1 are, and that means measuring text. Every measurement is a reflow, every reflow is a frame budget casualty.
With Pretext, you measure once with prepare() and then call layout() for each row width as the user resizes or scrolls. Heights are exact, scroll position is stable, and there's zero DOM round-trip cost.
Masonry grids, flowing text around dragged sprites, magazine-style multi-column layouts that respect arbitrary obstacles — these require knowing line-by-line geometry before committing to a layout. CSS can't tell you "how tall would this paragraph be at width X" without actually rendering it. Pretext can, in microseconds, for any number of trial widths.
walkLineRanges() and layoutNextLine() make iterative and variable-width layouts practical, including binary-searching for the optimal container width.
Designers care about whether a label fits before it gets clipped. With DOM measurement, you can only check after the fact — and only on devices identical to your test machine's font stack. Pretext lets you assert layout invariants in unit tests, in build scripts, or live in your design tooling, using exactly the font metrics the browser would use.
CLS — Cumulative Layout Shift — is one of Google's Core Web Vitals, and a major source of it is text that loads asynchronously and bumps everything down. If you know the final height up front (because you measured it without rendering it), you can reserve space exactly. No skeleton-screen guessing, no wrong placeholders, no jumpy layout.
The full surface area is small enough to memorize:
import { prepare, layout, layoutWithLines, walkLineRanges, layoutNextLine } from '@chenglou/pretext';
// 1. One-time measurement
const prepared = prepare("Hello, world — this is Pretext.", "18px Georgia");
// 2. Hot-path layout: returns just height + line count
const { height, lineCount } = layout(prepared, 400, 1.5);
// 3. Need per-line data for rendering?
const { lines } = layoutWithLines(prepared, 400, 1.5);
// 4. Iterate lines without building strings
walkLineRanges(prepared, 400, (start, end, lineIndex) => { /* ... */ });
// 5. Variable widths (e.g., flowing around an obstacle)
const line = layoutNextLine(prepared, lastEnd, currentMaxWidth);
prepare() is the only "expensive" call — and even then, ~19ms for 500 distinct strings on a laptop. layout() is fast enough to call inside requestAnimationFrame for every visible row.
Pretext is built for the messy reality of global typography:
white-space: pre-wrap mode for textarea-like behavior with preserved whitespace.It's not trying to be a full font rendering stack — it's a layout engine. Render targets are your choice: DOM, <canvas>, SVG, WebGL, or server-side via Node.
A subtle but important Pretext property: layout is deterministic and inspectable from JavaScript. That means an LLM (or any code generator) can produce a UI, ask Pretext "would this text overflow?", and iterate — no headless browser, no screenshot-based feedback loop. For automated UI generation, layout-aware code review, or design-system linting, that's a step change.
npm install @chenglou/pretext
No DOM, no peer dependencies, no framework lock-in. It works the same in React, Vue, Svelte, Solid, or plain JavaScript.
The best demonstration of why this matters is what people have built with it. The pretext.cool community showcase currently hosts 18+ interactive demos — typographic Tetris and Breakout, real-time text reflow around dragged sprites, the Star Wars opening crawl with correct perspective, magazine layouts flowing around animated clocks, face-tracked typography that responds to your webcam.
These aren't possible with traditional DOM measurement. They're not possible with CSS alone. They're possible because text layout finally got fast enough to be a per-frame primitive.
If you want to start building, the getting started guide walks you through your first layout in under five minutes, and the Pretext playground lets you try the API live in the browser.
Inspired by community write-ups about Pretext. Library by chenglou — community showcase at pretext.cool.