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.

51,045 stars2,757 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 a measurement path that does not use canvas, since the README describes none of those.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 30 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • chenglou/pretext51,045 ★ · +1,583
Star history
Repository2026-07-282026-11-14
chenglou/pretext49.5k51k

1,583 Stars in the last 109 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial 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 describes using the browser's own font engine as ground truth as a "very AI-friendly iteration method". 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 at its break opportunities, 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 http://localhost:3000/demos 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'.

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. 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.

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'. The wordBreak option only supports 'normal' and 'keep-all'. 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. The library measures segments with canvas, so if you need to measure text in Node.js without a canvas implementation, check whether it fits before adopting. 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 approaches that measure text with canvas, like 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. The opening of the README shows no benchmark or feature comparison table, so test it against your own text samples.

Maintenance and license

The repository is not archived. The latest release is v1.0.0, published 2026-10-14, and the last push is 2026-10-15. The package.json declares the license as MIT, although GitHub's metadata reports it as NOASSERTION, so confirm the LICENSE file before relying on it. The package is published on npm as @chenglou/pretext and the repository includes a CHANGELOG.md, so updates arrive through the normal npm channel. The package.json points its types field at ./dist/layout.d.ts, so type definitions ship with the package. The API is small, but 1.0.0 is a recent release, so pin the version and read the changelog before upgrading.

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 a measurement path that does not use canvas, since the README describes none of those. 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.

Frequently asked questions

how to install pretext

From npm the install is `npm install @chenglou/pretext`, a scoped package published with public access and its manifest version at 1.0.0. The demos are not part of that package, so to read them you clone the repository, run `bun install`, then `bun start`, and open http://localhost:3000/demos, with a separate start script for Windows.

how to use pretext js

It serves two use cases. Call prepare() once for a piece of text and a font to get an opaque handle, then call layout() with a max width and line height for the height and line count as pure arithmetic with no DOM reflow. For manual line placement, swap in prepareWithSegments and use layoutWithLines(), measureLineStats(), walkLineRanges(), or layoutNextLineRange() with a cursor.

how to use pretext library

Measure a paragraph's height without touching the DOM, then use that number for virtualization, userland layouts, or checking labels in development. For text mixing fonts, code spans, mentions, or chips, use the separate rich-inline entry point, which takes a flat list of text items and is explicitly not a general CSS inline formatting engine.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chenglou-pretext.svg)](https://hysenlabs.com/projects/chenglou-pretext)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/chenglou-pretext.svg)](https://hysenlabs.com/projects/chenglou-pretext)

Was this useful?