Pretext
View on GitHubTypeScript library for multiline text measurement and layout without DOM reflow.
About
Pretext solves a problem that has frustrated web developers for thirty years: there is no reliable way to know how text will wrap before it hits the DOM. The standard approaches — getBoundingClientRect, offsetHeight, querying computed styles — all trigger synchronous layout reflow. Measure 500 text blocks at once and you are looking at 30ms or more per frame, before you have rendered anything.
Pretext takes a different path. It uses the Canvas API's measureText() as a proxy for the browser's font engine, caches the results, and reduces all subsequent layout work to arithmetic on those cached widths. The result is a two-phase design: prepare() does the expensive measurement once, layout() runs in approximately 0.0002ms per block with no DOM access on the hot path.
The library handles the scripts that make browser text layout genuinely hard. Unicode segmentation via Intl.Segmenter handles CJK (where each character is a potential break point), Thai (which lacks spaces between words), and Arabic and Hebrew (which run right-to-left and require the Unicode Bidirectional Algorithm). Seven source files, roughly 15KB of TypeScript, covering most of what a production text layout engine needs to handle.
It is a small library with a precise scope. It does not replace CSS. It gives you line break positions and character offsets as numbers, which you can render however you like — canvas, SVG, custom DOM, WebGL. The decision about what to do with those numbers is yours.
Growing Ecosystem: Beyond the Browser
Five days after Pretext launched, native ports started appearing. swift-pretextkit by Tornike Gomareli brings Pretext's two-phase text measurement to macOS and iOS, with the creator claiming halved CPU and power usage compared to existing options in the Apple ecosystem. The speed of this port signals that Pretext's core insight — separate measurement from layout, cache aggressively — is not browser-specific. It is a general approach to text layout that translates across platforms.
Further Reading
Pretext, and What's Next — an analysis of where Pretext fits in the broader landscape and what the native ports mean for the ecosystem.
Architecture
Two phases, one insight:
prepare(text, options) runs once per unique text string and font configuration. Calls Intl.Segmenter for script-aware segmentation, measures each segment via Canvas measureText(), and stores widths in a two-level cache keyed by font string and segment.
layout(preparedText, containerWidth) is pure arithmetic. Walks the cached widths, applies the greedy line-breaking algorithm, and returns line break positions and character offsets. No DOM. No Canvas calls. No side effects.
| File | Size | Role |
|---|---|---|
layout.ts | 24KB | Public API surface |
line-break.ts | 31KB | Core greedy line-breaking algorithm |
analysis.ts | 27KB | Unicode script analysis |
measurement.ts | 7KB | Canvas measurement and width cache |
bidi.ts | 6KB | Bidirectional text (Arabic, Hebrew) |
layout.test.ts | 36KB | Test suite |
test-data.ts | 5KB | Ground-truth test fixtures |
The test suite is larger than the implementation. That is not an accident — browser behavior around text is inconsistent enough that the tests document a set of known browser discrepancies as much as they verify correctness.
Start here
If you are new to Pretext, start with the Grand Tour. It walks through all seven source files in dependency order, from the public API in layout.ts down to the Canvas measurement cache and the Unicode segmentation logic. You will come out understanding why the two-phase design exists, how the cache is structured, and what the browser inconsistencies documented in RESEARCH.md actually look like in practice.
Tours
Maintainers
Currently at Midjourney. Not a newcomer to problems at the intersection of browser constraints and developer experience. His library react-motion (21,700+ stars) replaced the dominant CSS animation approach with physics-based spring animations. Spent several years at Meta working on ReasonML and ReScript, bringing ML-family type systems to the JavaScript ecosystem. His 2016 React Europe talk, "On the Spectrum of Abstraction," laid out a thesis he has returned to since: reducing expressivity through the right constraints yields more capability, not less.
Origin Story
Pretext: Measuring Text Without the DOMHow a 30-year browser problem met a 15KB TypeScript library -- and why 31,000 developers starred it in 25 days.
Read the full storyRelated Projects
Create code tours for your project
Intraview lets AI create interactive walkthroughs of any codebase. Install the free VS Code extension and generate your first tour in minutes.
Install Intraview Free