Library / SDK
chenglou/pretext avatar
chenglou/pretext

Pretext: measuring multiline text without touching the DOM

Pretext measures and lays out multilingual text in web applications, using Canvas, DOM, and OpenType data to produce predictable line breaks and dimensions.

50,362 stars2,737 forksTypeScriptMIT

At a glance

What is it?
Pretext is a TypeScript library that measures and lays out multiline text using Canvas, DOM, and OpenType data. It avoids layout reflow by doing pure arithmetic after a one-time preparation pass.
Who is it for?
Adopt Pretext if you build text-heavy UIs where DOM measurement causes reflow costs, such as virtualized lists, canvas or SVG renderers, or AI-assisted development-time checks on label overflow. Skip it if you need automatic hyphenation, a full CSS inline formatting engine, or server-side rendering today, since those are not present.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem: measuring text without triggering layout

Web developers measure text with getBoundingClientRect or offsetHeight. Those calls force the browser to compute layout, which is one of the most expensive operations in a page. Pretext takes a different route. It implements its own text measurement logic and uses the browser's font engine as the ground truth. The README calls this an "AI-friendly iteration method", meaning you can get consistent measurements without depending on the DOM. The library is aimed at anyone building virtualized lists, masonry layouts, canvas or SVG renderers, or any UI where text height and line breaks must be known before rendering. It does not replace the browser's layout engine for interactive documents. It replaces a specific measurement pattern that causes reflow.

Two APIs: one for height, one for line ranges

Pretext separates one-time analysis from the hot path. The prepare() function takes a string, a font string, and optional whitespace, word-break, and letter-spacing options. It normalizes whitespace, segments the text, applies glue rules, and measures segments with canvas. It returns an opaque handle. The layout() function then computes height and line count from that handle, a max width, and a line height. The README stresses that you should not rerun prepare() for the same text and configs. On resize, only layout() runs. That split is the core design. The second use case, prepareWithSegments(), returns a richer structure. From it you get layoutWithLines(), measureLineStats(), walkLineRanges(), and layoutNextLineRange(). These give you line strings, widths, cursors, and the ability to flow text one row at a time when the width changes as you go. The README's example routes text around a floated image by changing the width based on the current y position.

Getting it running: install and demos

Installation is one command: npm install @chenglou/pretext. The package is pure JavaScript or TypeScript, so no build step beyond your normal toolchain. To see the demos, clone the repo, run bun install, then bun start, and open /demos/index in your browser. On Windows, the command is bun run start:windows. The live demos are at chenglou.me/pretext, with additional ones at somnai-dreams.github.io/pretext-demos. The API examples in the README show TypeScript imports like import { prepare, layout } from '@chenglou/pretext'. The font string format matches what you would assign to myCanvasContext.font, for example '16px Inter'. The letterSpacing option is a CSS pixel value. The whiteSpace option accepts 'normal' or 'pre-wrap'. The wordBreak option accepts 'normal' or 'keep-all'. There is no mention of a bundler requirement or a Node.js version constraint in the README.

Manual layout and the rich-inline helper

For canvas, SVG, or WebGL rendering, you can skip the DOM entirely. The layoutWithLines() function returns line objects with text, which you can pass to ctx.fillText(). For more control, layoutNextLineRange() gives you a range from a cursor, and materializeLineRange() turns that range into a full string. This lets you flow text into arbitrary shapes, like the floated-image example. The library also ships a helper at @chenglou/pretext/rich-inline. It handles inline flow for rich text, code spans, mentions, chips, and browser-like boundary whitespace collapse. You pass an array of fragments, each with text, font, an optional break: 'never' for atomic items, and an optional extraWidth for pill chrome. The helper is intentionally narrow. It only supports white-space: normal. It is not a nested markup tree and not a general CSS inline formatting engine. The README is explicit that it stays inline-only on purpose.

Hyphenation: a manual, conservative approach

Pretext does not include automatic hyphenation. The README says so directly. For manual layout, you insert soft hyphens before calling prepare() or prepareWithSegments(). Pretext treats them as optional break points. Unchosen soft hyphens stay invisible. Chosen breaks materialize as a trailing hyphen. The README advises conservative, locale-aware insertion for mixed-language or user-generated text, rather than aggressive pattern hyphenation. This is a deliberate trade-off. If your application needs automatic hyphenation for justified text in narrow columns, you would have to build that yourself on top of the library. The README does not describe any built-in support for hyphenation dictionaries or language detection.

Limitations and when it is the wrong tool

The library's strength is predictable measurement without DOM layout. But that comes with constraints. The font string and letterSpacing must match what your CSS uses, or the measurements will be wrong. The README warns about this directly. The whiteSpace option only supports 'normal' and 'pre-wrap'. There is no mention of 'nowrap' or 'pre'. The wordBreak option only supports 'normal' and 'keep-all'. No 'break-all' or 'break-word' equivalents are listed. For textareas, the pre-wrap mode keeps spaces, tabs, and newlines visible, but you must pass that option explicitly. The rich-inline helper only works with white-space: normal, so it cannot handle pre-formatted text. Server-side rendering is listed as "soon" in the description, not as a current feature. If you need to measure text in Node.js without a canvas, this library will not help you yet. Also, the library does not handle automatic hyphenation, so long unbroken strings in narrow containers may overflow unless you insert soft hyphens yourself.

Alternatives: DOM measurement and CSS-based approaches

The obvious alternative is to use the browser's built-in measurement APIs, such as getBoundingClientRect or offsetHeight. That works everywhere but triggers layout reflow, which is exactly what Pretext avoids. Another alternative is to render text with CSS and let the browser handle wrapping, then read the height after the fact. That is simple but still causes layout and is hard to use for canvas or SVG. A more direct comparison is with libraries that do text measurement using canvas, like fontmetrics or the canvas measureText API. Those give you width per string but not full paragraph layout with line breaking and glue rules. Pretext goes further by segmenting text and applying glue rules, which is what makes its height calculation pure arithmetic after preparation. The README also mentions that Pretext supports all the languages you didn't even know about, which suggests it handles complex scripts and mixed-direction text better than a naive measureText approach. But the README does not show a benchmark or a feature comparison table, so you would need to test it against your own text samples.

Maintenance and license

The repository is archived? No, the metadata says archived: no. But there are no recent releases listed, and the last push is unknown. That is a signal to check the repository directly before adopting. The license is MIT, which means you can use it in commercial projects with attribution. The README does not describe a contribution process or a release cadence. The package is published on npm as @chenglou/pretext, so updates would come through the normal npm channel. The codebase is TypeScript, so type definitions are likely included, but the README does not say explicitly. Given the lack of release history in the README, you should verify that the package is actively maintained if you plan to depend on it for a long-lived project. The API is small and stable-looking, so the risk may be low, but the maintenance picture is unclear from what is available.

Editorial conclusion

Adopt Pretext if you build text-heavy UIs where DOM measurement causes reflow costs, such as virtualized lists, canvas or SVG renderers, or AI-assisted development-time checks on label overflow. Skip it if you need automatic hyphenation, a full CSS inline formatting engine, or server-side rendering today, since those are not present. Before adopting, verify that the font string and letterSpacing values you pass match what your CSS uses, and confirm that the library's whitespace and word-break options cover your text cases, especially for pre-wrap textareas and mixed-language content.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
Community notes

Community notes