← All writing

I found a text layout library in an Instagram reel

26 August 2026 · 4 min read

I am not a frontend developer 🙂. I write PHP for a living: WordPress plugins, Laravel services, the occasional stored procedure I would rather not discuss. There is plenty of JavaScript in that: jQuery by the yard, vanilla JS, some React and Next when a project calls for it. But nobody would mistake me for a frontend specialist, and layout engines are several streets past where I usually work.

Which is why it is faintly ridiculous that I lost an evening to a text layout library I found on Instagram. The algorithm mostly shows me football and people restoring rusted tools. Somewhere between the two: a fifteen second clip of text reflowing in a browser, captioned something about measuring text without touching the DOM. I watched it three times, then went looking for it.

Pick the card up and move it. Every line re-routes around it as it goes. Let go and it drifts on, bouncing off the edges and turning as it travels.

That is pretext, by Cheng Lou — the same person behind react-motion, and someone who has been thinking about interface plumbing for a very long time. The demo above is my own small version of his dynamic-layout demo; the good idea is entirely his.

Why this is a library and not a one-liner

If you have ever needed to know how tall a paragraph will be before it renders, you know the standard answer: put it in the DOM, read offsetHeight, take it back out. It works, and it forces a synchronous layout every single time. Do it in a loop, over a list, on resize, and you have built a jank machine 😬.

Pretext does the measuring itself: segment the text, measure the pieces once, then answer questions with arithmetic.

js
const prepared = prepare('AGI 春天到了. بدأت الرحلة', '16px Inter')
const { height, lineCount } = layout(prepared, 320, 20)   // no reflow

prepare() does the expensive part once. layout() is cheap enough to call on every resize frame. That unlocks things normally left to guesswork: virtualised lists that do not jump, masonry that actually fits, checking a button label does not wrap before you ship it.

The part that hooked me

CSS can float a box and wrap text beside it. What it cannot do is route a paragraph around an arbitrary shape that moves, because the browser owns line breaking and you are not invited to the decision.

The loop is smaller than you would expect:

js
let cursor = { segmentIndex: 0, graphemeIndex: 0 };

while (y + lineHeight <= height) {
  const slots = carveSlots(region, obstaclesInBand(y, y + lineHeight));

  for (const slot of slots) {
    const range = layoutNextLineRange(prepared, cursor, slot.right - slot.left);
    if (range === null) break;                     // text exhausted
    ctx.fillText(materializeLineRange(prepared, range).text, slot.left, y);
    cursor = range.end;                            // carry on where it stopped
  }
  y += lineHeight;
}

The cursor is the whole idea 🤔. Each call lays out one line at whatever width that band happens to allow, then hands back where it stopped, and you decide where the next one goes. Two columns are the same loop with a shared cursor. Text wrapping around a picture is a narrower slot for a few bands.

Line breaking stops being something the browser does to your text, and becomes something you decide, one line at a time, against whatever is in the way.

And prepareWithSegments() runs once. Everything happening while you move the cursor is arithmetic over widths already measured, which is why it does not stutter.

Honest notes

A few things worth knowing before reaching for it:

Thing Reality
Server-side rendering not yet. It needs OffscreenCanvas or a DOM canvas, so my build script cannot use it 😕
Bundle about 226KB of ESM, self-hosted here rather than pulled from a CDN
Setup relative imports with extensions, so it loads natively in the browser with no bundler at all

That last one deserves credit. The dist is plain ESM with fully specified import paths, so I copied it into the build output and imported it directly. No webpack, no config, no step. More libraries should ship like this 😌.


I still have no use for pure text measurement. Nothing in my week involves routing a paragraph around a moving badge, and I am not about to become a typography person.

But that is not really why any of us follow these threads. Somebody solved a hard problem carefully, published it, and made it easy to try. Spending an evening inside someone else's good idea is how you end up with taste, even in the parts of the stack you do not work in 😄.

Not bad for something the algorithm dropped between a goal compilation and a man sanding a plane blade.

javascripttypographyperformance