
The Future of Website Interfaces: Pretext Package
What is this?
Pretext is a single JavaScript/TypeScript package that lets you measure and lay out text without relying on the browser's layout engine.
Every website you've ever used asks the browser to figure out where text goes on the page. That process (called "reflow") is one of the slowest things a browser does. It's been the biggest bottleneck in web design for 30 years.
Pretext skips that entirely. It does all the text measurement in pure TypeScript โ no DOM, no CSS layout, no reflow. Just math.
The result: Hundreds of thousands of text elements rendering at 120fps without a single lag.
๐ Links
- GitHub Repo: github.com/chenglou/pretext
- Live Demos: chenglou.me/pretext
- More Demos: somnai-dreams.github.io/pretext-demos
๐ฅ What the demos show
| Demo | What it does |
|---|---|
| Masonry | Hundreds of thousands of text boxes, all different heights, scrolling at 120fps with zero DOM measurement |
| Chat Bubbles | Shrinkwrapped chat bubbles that size themselves perfectly |
| Magazine Layout | Multi-column responsive magazine layout โ dynamic and real-time |
| ASCII Art | Variable font width ASCII art because why not |
| Accordion | Auto-growing text areas, accordions, multiline centering โ things that used to be real CSS challenges, now trivial |
๐ง Why this matters (simple version)
- Every website asks the browser permission to place text. That permission is slow. It causes lag. It's called "reflow."
- Pretext skips that permission entirely. It calculates text layout in pure TypeScript.
- The performance difference is insane. 0.05ms vs 30ms. Zero reflows vs hundreds.
- This unlocks UI that wasn't possible before. Smooth virtualization, masonry layouts, magazine-style text, shrinkwrapped elements โ all without CSS hacks.
โก Quick Start
Install
npm install @chenglou/pretextBasic usage โ measure text height without touching the DOM
import { prepare, layout } from '@chenglou/pretext'
const prepared = prepare('Your text here', '16px Inter')
const { height, lineCount } = layout(prepared, maxWidth, 20)
// That's it. No DOM. No reflow. Pure math.How it works
prepare()โ one-time work: analyzes the text, measures segments using the browser's font engine, returns a cached handle
layout()โ the fast part: pure arithmetic over cached widths. Run this on resize, scroll, or any layout change
Key rule: Don't rerun prepare() for the same text. That defeats the precomputation. Only rerun layout().
๐งช Prompts to try with AI
Use these with Claude, ChatGPT, or Cursor to start building with Pretext:
Prompt 1 โ Basic integration
"I want to use @chenglou/pretext to measure text height without DOM reflow. Show me how to set up prepare() and layout() for a list of 1000 items with variable text lengths, and use the heights for virtualized scrolling."
Prompt 2 โ Masonry layout
"Using @chenglou/pretext, help me build a masonry grid layout where each card contains variable-length text. I want to calculate all card heights upfront without any DOM measurement, then position them in columns using absolute positioning."
Prompt 3 โ Chat interface
"I'm building a chat interface. Using @chenglou/pretext and walkLineRanges(), help me shrinkwrap each chat bubble to the exact width of its longest line instead of using a fixed max-width. I want the bubbles to feel tight and natural."
Prompt 4 โ Canvas rendering
"Using @chenglou/pretext's layoutWithLines(), help me render multiline text directly to an HTML canvas with proper line breaking and word wrapping. I want to bypass DOM text rendering entirely."
Prompt 5 โ Magazine layout
"Help me build a responsive multi-column magazine-style text layout using @chenglou/pretext's layoutNextLine(). The text should flow around an image floated in the first column, with narrower line widths beside the image and full-width lines below it."
๐ Full API Reference
Use case 1: Measure height only (fastest)
prepare(text, font, options?)
// Returns opaque PreparedText handle
layout(prepared, maxWidth, lineHeight)
// Returns { height, lineCount }Use case 2: Manual line layout (full control)
prepareWithSegments(text, font, options?)
// Returns richer structure for manual layout
layoutWithLines(prepared, maxWidth, lineHeight)
// Returns { height, lineCount, lines[] }
walkLineRanges(prepared, maxWidth, onLine)
// Calls onLine per line with width + cursors โ no string building
layoutNextLine(prepared, start, maxWidth)
// Iterator-style โ one line at a time with variable widthsHelpers
clearCache()
// Clears internal caches if cycling through many fonts
setLocale(locale?)
// Sets locale for future prepare() callsโ ๏ธ Things to know
- Currently targets standard text setup:
white-space: normal,word-break: normal,overflow-wrap: break-word
- Supports
{ whiteSpace: 'pre-wrap' }for textarea-like text with preserved spaces, tabs, and line breaks
- Avoid
system-uifont on macOS โ use a named font for accuracy
- Supports all languages including emoji and mixed bidirectional text
- Not a full font rendering engine (yet) โ it's focused on measurement and layout
๐จโ๐ป Who built this
Cheng Lou โ helped build React at Facebook, created ReasonML and ReScript, built Messenger's frontend, currently runs Midjourney's entire UI stack. Every role was a fight against the browser's rendering pipeline. This is the result.