Open Source
Pretext: Measuring Text Without Touching the DOM
By DI Solutions
Developer


Pretext is a zero-dependency TypeScript library that tells you how tall a block of wrapped text will be, and exactly where each line breaks, without putting anything in the DOM. It reimplements the browser's line-breaking algorithm in userland, using the browser's own font engine as the measuring instrument.
It exists because of a very specific problem at Midjourney: streaming text that grows as tokens arrive, in a UI that has to know each block's height to lay itself out.
Key takeaways
- Measuring text via the DOM forces a synchronous reflow. In a loop, that is layout thrash and dropped frames.
- Pretext uses Canvas
measureText, which asks the font engine directly and triggers no layout, plusIntl.Segmenterfor correct word and grapheme boundaries. - Measurement happens once in a prepare step; laying out at a given width afterwards is close to pure arithmetic.
- It handles CJK, Arabic and emoji correctly — the cases hand-rolled loops get wrong — with no dictionary shipped.
- It can lay text around irregular shapes, which CSS itself barely manages.
- Enormous star count, version 0.0.8, and a publishing gap. Pin the version and read its limitations list.
Why is measuring text expensive?
Because you are not measuring — you are forcing a synchronisation. The browser batches style and layout work and flushes it when convenient. Read offsetHeight and you are telling it: stop, compute all of that now, I need an answer.
Once, that is nothing. Inside a loop it is the classic layout thrash pattern: write, read, write, read, each read forcing a full flush of the writes before it. A hundred measurements can cost more than rendering the entire page.
Where does that actually bite? Four places, all common:
- Virtualized lists with variable row heights. The virtualizer needs a height before it renders the row, so you either guess and correct with a visible jump, or measure and pay for it.
- Canvas and WebGL interfaces. There is no DOM to measure against at all. You are writing your own layout by definition.
- Charts. Label collision detection needs to know how wide every label is before deciding which to draw.
- Server-side rendering. Generating a PDF or an email on a server means computing text height with no browser anywhere.
And the origin case: text streaming in token by token. The block grows continuously, the layout must follow, and every frame wants a fresh height.
How does Pretext work?
Two APIs and one important split.
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20)prepare does the expensive part once: segment the string into words and graphemes with Intl.Segmenter, then measure the pieces with Canvas measureText. Neither of those touches layout. measureText goes straight to the font engine, which is exactly why it is cheap.
layout then takes a width and a line height and walks the measured pieces, applying line-breaking rules. It is arithmetic over data you already have. Laying the same text out at eight different widths — say, to find the one that fits — is nearly free.
Beyond the height-only path there is a lower-level API for per-line control, including a mode that computes line count and maximum width without allocating, and an iterator that walks lines without building strings. Those exist for code that runs every frame, and the fact that they exist tells you what the library is really for.
The genuinely interesting part: how it was built
Pretext's author is Cheng Lou — React core team at Facebook in the mid-2010s, creator of react-motion, and one of the people behind ReasonML and ReScript. He now works at Midjourney.
He has said openly that the library was largely written by AI models, and the method is more interesting than the headline. He did not ask a model to write a text layout engine. He built a differential test harness that rendered text in a real browser, captured the browser's own line breaks as ground truth, and iterated the TypeScript implementation against that oracle across large multilingual corpora until the outputs matched.
That is the transferable lesson. The models did not know how browsers break lines. The harness did — because the browser was right there, able to answer. The engineering contribution was recognising that the correctness signal already existed and could be automated.
Anywhere you have a slow reference implementation and want a fast one, that pattern applies. It is a much better argument for AI in engineering than any benchmark.
What it does not do — from its own README
The limitations list is unusually candid, and worth reading before you adopt it:
| Limitation | What it means for you |
|---|---|
| Horizontal text only | No vertical writing modes. Rules out some CJK typography. |
| Only the canvas font shorthand is modelled | Font feature settings and optical sizing are not reflected in the measurement. |
system-ui is unreliable on macOS | Name a real font stack rather than relying on the keyword. |
| Very narrow widths break inside words | Breaks fall at grapheme boundaries. Test your narrowest breakpoint. |
Requires Intl.Segmenter and Canvas | Fine in modern browsers; check your support floor. |
The README also states plainly that it is not trying to be a full font rendering engine. That is a healthy scope boundary and a reason to trust the rest of the documentation.
A note on 50,000 stars and version 0.0.8
This is the clearest example in this whole series of stars and maturity being different axes. Pretext has an enormous star count, an eight-line README example, a version number of 0.0.8, and — as of late August 2026 — no npm publish for around two and a half months.
That is not a criticism of the code, which is well-scoped and honest about itself. It is a statement about risk. Stars measure how many people thought something was interesting, which is weakly correlated with how many people are running it in production and reporting bugs.
Practical advice: pin the exact version, wrap it behind a small interface of your own, and be genuinely prepared to fork it. That is cheap here because the scope is narrow. We go further into this in how to pick open source you can actually ship.
Alternatives worth knowing
- Measure in the DOM, but batch it. Genuinely the first thing to try. Group all your reads, then all your writes, and much of the thrash disappears without a dependency.
- Your virtualizer's dynamic measurement. TanStack Virtual and react-window handle variable heights with a measure-then-correct pass. Good enough for most lists.
- opentype.js or fontkit — parse the font file and measure from the metrics. More capable, heavier, and you need the font binary.
- harfbuzzjs — real text shaping via WebAssembly. Far more correct for complex scripts, far larger, and overkill unless you are building a typesetter.
- Satori — if the actual goal is rendering text to an image on a server, this solves that directly rather than giving you measurements to act on.
Conclusion
Pretext is a sharp tool for a narrow problem. If you are fighting layout thrash in a virtualized list, laying out text on a canvas, or computing heights on a server, it is exactly the right shape. If you measure a couple of elements on mount, the DOM is fine.
And however you feel about AI-written libraries, the way this one was built — automating a correctness oracle rather than trusting the model — is worth stealing.
Fighting frame drops in a React interface?
DI Solutions profiles and fixes front-end performance — layout thrash, oversized bundles, render storms — on real production React applications. Bring in our front-end engineers and find out where the milliseconds actually went.
Reference links
Frequently Asked Questions (FAQs)
What is Pretext?
Pretext is a zero-dependency TypeScript library that calculates how tall a block of wrapped text will be, and where each line breaks, without writing anything into the DOM. It reimplements the browser's line-breaking behaviour in userland.
Why is measuring text in the DOM slow?
Reading a property like offsetHeight forces the browser to flush pending style and layout work synchronously. Do that inside a loop and you get layout thrash — repeated forced reflows that drop frames. The cost is not the measurement, it is the synchronisation it forces.
How does Pretext measure without reflow?
It uses the Canvas 2D measureText API, which asks the font engine for advance widths and does not trigger layout, combined with Intl.Segmenter for correct word and grapheme boundaries. It then runs its own line-breaking algorithm over those measurements.
Does Pretext handle non-Latin scripts and emoji?
Yes. Using Intl.Segmenter for segmentation is what gives it correct behaviour for CJK, Arabic and emoji without shipping a dictionary. That is precisely where hand-rolled measure-and-break loops usually go wrong.
Is Pretext production ready?
Be careful. It has an enormous star count but is at version 0.0.8, and there was no npm publish for roughly two and a half months as of late August 2026. Pin the exact version and be prepared to own a fork if you depend on it.
What is Pretext not good at?
By its own documentation: horizontal text only, no vertical writing modes; CSS features outside the canvas font shorthand are not modelled; system-ui is unreliable for macOS accuracy; and very narrow widths may break inside words at grapheme boundaries.
When should I use Pretext instead of the DOM?
When you need text dimensions before painting, many times, or in a place with no DOM at all — virtualized lists with variable row heights, canvas and WebGL interfaces, chart label collision detection, and server-side rendering to PDF or email.




