Pure JavaScript/TypeScript library for multiline text measurement & layout. Fast, accurate & supports all the languages you didn't even know about. Allows rendering to DOM, Canvas, SVG and soon, server-side.
Pretext side-steps the need for DOM measurements (e.g. getBoundingClientRect, offsetHeight), which trigger layout reflow, one of the most expensive operations in the browser. It implements its own text measurement logic, using the browsers' own font engine as ground truth (very AI-friendly iteration method).
npm install @chenglou/pretextClone the repo, run bun install, then bun start, and open http://localhost:3000/demos in your browser. On Windows, use bun run start:windows.
Alternatively, see them live at chenglou.me/pretext. Some more at somnai-dreams.github.io/pretext-demos
Building a chat or another long list? pages/demos/markdown-chat.md walks through the Markdown chat demo's patterns and when you can skip each.
Pretext serves 2 use cases:
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20) // pure arithmetic. No DOM layout & reflow!prepare() does the one-time work: normalize whitespace, segment the text at its break opportunities, measure the segments with canvas, and return an opaque handle. layout() is the cheap hot path after that: pure arithmetic over cached widths. Do not rerun prepare() for the same text and configs; that'd defeat its precomputation. For example, on resize, only rerun layout().
If you want textarea-like text where ordinary spaces, \t tabs, and \n hard breaks stay visible, pass { whiteSpace: 'pre-wrap' } to prepare():
const prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' })
const { height } = layout(prepared, textareaWidth, 20)
// Long text edited live: prepare each paragraph apart, keeping its \n, and re-prepare only the one an edit touches