Mastering Pretext: A Practical Frontend Guide for Precise Text Measurement
This article explains how the open‑source Pretext library enables fast, accurate multi‑line text measurement and layout in pure JavaScript/TypeScript, outlines the problems it solves, lists ideal front‑end scenarios, provides step‑by‑step API usage, and warns when the library should be avoided.
Problem solved
Pretext avoids costly DOM measurements such as getBoundingClientRect and offsetHeight that trigger layout reflow. It uses the browser’s font engine via Canvas measureText to obtain true‑to‑render metrics and then performs pure arithmetic layout in memory.
The library follows a two‑stage design: prepare() performs a one‑time segmentation, measurement and caching for a given text‑font pair; layout() quickly computes block height and line count for a fixed maxWidth and lineHeight, making it cheap enough for high‑frequency calls such as window resize.
Front‑end scenarios where Pretext shines
Virtualized lists / windowing : precise item height is required for correct scrolling and DOM reuse; Pretext calculates heights in JavaScript, eliminating per‑cell DOM measurement.
Chat or feed messages : message bubble height varies with content; using prepare + layout to pre‑estimate height reduces layout jitter.
Infinite scroll with placeholder sizing : compute height from width and font before rendering real nodes to lessen cumulative layout shift.
Dynamic width layout : when sidebars or responsive breakpoints change, only layout() needs to be re‑run on the same prepared data.
Prevent layout shift after text loads : pre‑compute height with the same font and container width to reserve space or lock scroll anchors.
Custom "flex‑like" layout : predictable line breaks and line heights become inputs for custom layout algorithms.
Masonry / grid cards : use measured line heights to drive column allocation without runtime DOM probing.
Canvas, SVG, WebGL text drawing : layoutWithLines returns per‑line strings and widths for manual rendering.
Complex or multilingual rich text : supports emoji, mixed scripts, and CSS‑compatible white‑space handling.
When not to use or use cautiously
Full‑page layout can be handled by CSS; avoid adding Pretext when no virtual list or Canvas needs exist.
Pretext does not replace the browser’s CSS box model; it only measures text at the line level.
On macOS, avoid the generic system-ui font for measurement; use a named font family for reliable results.
The rich-inline sub‑package only supports white-space: normal and inline flow; it is not a general rich‑text engine.
Installation
npm install @chenglou/pretextCore usage 1 – height and line count
Typical for virtual list items, textarea‑style auto‑height, or responsive re‑layout.
import { prepare, layout } from '@chenglou/pretext'
// Keep CSS font consistent, e.g. "font: 16px Inter"
const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀', '16px Inter')
const { height, lineCount } = layout(prepared, textWidth, 20) // 20 = line‑height in pxKey points: call prepare() once per text‑font combination; call layout() whenever width or line‑height changes.
Core usage 2 – manual per‑line layout (Canvas / SVG / variable width)
Suitable for custom drawing where each line may have a different width.
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
const prepared = prepareWithSegments('AGI 春天到了. بدأت الرحلة 🚀', '18px "Helvetica Neue"')
const { lines } = layoutWithLines(prepared, 320, 26) // 320 px width, 26 px line‑height
lines.forEach((line, i) => ctx.fillText(line.text, 0, i * 26))Other helpers such as measureLineStats and walkLineRanges let you obtain line count, maximum line width, or perform width‑search without materializing full strings.
Rich‑inline support
For a single line containing multiple font fragments, mentions, or chips, use the @chenglou/pretext/rich-inline sub‑package.
import { prepareRichInline, walkRichInlineLineRanges, materializeRichInlineLineRange } from '@chenglou/pretext/rich-inline'
const prepared = prepareRichInline([
{ text: 'Ship ', font: '500 17px Inter' },
{ text: '@maya', font: '700 12px Inter', break: 'never', extraWidth: 22 },
{ text: "'s rich-note", font: '500 17px Inter' }
])
walkRichInlineLineRanges(prepared, 320, range => {
const line = materializeRichInlineLineRange(prepared, range)
// line contains fragments with itemIndex, gapBefore, occupiedWidth, etc.
})Limitation: only works with white-space: normal and inline flow; it is not a full CSS inline engine.
Other APIs and maintenance
clearCache() // release internal caches when many font or text variants are used
setLocale(locale?) // affects subsequent prepare calls and clears cache; existing handles are unchangedChecklist for CSS alignment
Ensure the font string matches canvas.font and CSS font shorthand.
Pass the same pixel value used in CSS for lineHeight to layout().
Set maxWidth to the actual content width after padding.
Keep whiteSpace and wordBreak options consistent with page rules.
Caveats
Pretext is not a full font‑rendering engine; it targets common web text configurations such as white-space: normal, pre-wrap, word-break: normal / keep-all, overflow-wrap: break-word, and line-break: auto. Tab size defaults to 8. Soft‑hyphen breaks may leave a visible trailing "-". For complex bidirectional scripts or custom drawing, consult the README notes on bidi handling.
Learning resources
Repository README (full API) – github.com/chenglou/pretext
Online demo – chenglou.me/pretext
Additional demos – somnai-dreams.github.io/pretext-demos
Selection table (when to use which API)
Need only block height and line count → prepare + layout Fixed width, need each line string (Canvas/SVG) → prepareWithSegments + layoutWithLines Probe width / shrink‑wrap / no string needed → measureLineStats, walkLineRanges Variable line width per line → layoutNextLineRange + materializeLineRange Multiple fonts inline with chips/mentions →
@chenglou/pretext/rich-inlineSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Frontend AI Walk
Looking for a one‑stop platform that deeply merges frontend development with AI? This community focuses on intelligent frontend tech, offering cutting‑edge insights, practical implementation experience, toolchain innovations, and rich content to help developers quickly break through in the AI‑driven frontend era.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
