npm: react-pretext-hooks
Pretext by Chen Lou is the library worth knowing about: canvas-backed text measurement, line breaking, and layout that keeps the expensive work off the DOM, with typography that stays faithful to how browsers render text.
react-pretext-hooks brings @chenglou/pretext into React with a few hooks (prepare / layout memoization, ResizeObserver where it helps) and a small <MeasuredText> component. Capabilities and design live in the Pretext repo, this is just convenience wiring.
npm install react-pretext-hooks @chenglou/pretext react@chenglou/pretext and react (>=18) are peer dependencies.
All-in-one hook. Returns { height, lineCount }.
import { useTextLayout } from "react-pretext-hooks";
function MyComponent() {
const { height, lineCount } = useTextLayout(
"Hello world, this is a long paragraph…",
"16px/1.5 Inter",
300,
24,
);
return <div style={{ height }}>…</div>;
}Returns the raw PreparedText handle for power users who want to call layout() manually at multiple widths.
import { usePrepared } from "react-pretext-hooks";
import { layout } from "@chenglou/pretext";
function MyComponent({ widths }: { widths: number[] }) {
const prepared = usePrepared("Some text", "16px Inter");
const layouts = widths.map((w) => layout(prepared, w, 24));
// …
}ResizeObserver-aware hook. Attach it to a container ref and it recomputes layout automatically when the container resizes.
import { useRef } from "react";
import { useAutoLayout } from "react-pretext-hooks";
function AutoBox() {
const ref = useRef<HTMLDivElement>(null);
const { height, lineCount, width } = useAutoLayout(
"Resizable text…",
"16px Inter",
24,
ref,
);
return (
<div ref={ref} style={{ height }}>
Resizable text…
</div>
);
}Component wrapper around useAutoLayout. Renders a container whose height is driven by the measured text layout.
import { MeasuredText } from "react-pretext-hooks";
<MeasuredText text="Hello world" font="16px Inter" lineHeight={24}>
<p>Hello world</p>
</MeasuredText>;Props:
| Prop | Type | Description |
|---|---|---|
text |
string |
The text to measure. |
font |
string |
CSS font shorthand (e.g. "16px Inter"). |
lineHeight |
number |
Line height in pixels. |
options |
PrepareOptions |
Optional whiteSpace / wordBreak overrides. |
children |
ReactNode |
Rendered inside the container (defaults to text). |
className |
string |
Class name for the wrapper element. |
style |
CSSProperties |
Additional inline styles. |
as |
keyof JSX.IntrinsicElements |
HTML tag to render (default "div"). |
The component also sets data-measured-height, data-measured-lines, and data-measured-width attributes on the wrapper element.
interface PrepareOptions {
whiteSpace?: "normal" | "pre-wrap";
wordBreak?: "normal" | "keep-all";
}@chenglou/pretext splits text measurement into two phases:
prepare()— the expensive step. Segments text and measures each segment via an offscreen canvas. This is memoized by the hooks so it only runs whentextorfontchange.layout()— pure arithmetic (~0.0002 ms). Computes height and line count from the prepared data for a given width. This runs on every resize or parameter change with negligible cost.
MIT