npm package: @pretextui/core
Description: React components powered by [Pretext](https://github.com/chenglou/pretext) — virtual scrolling, auto-resize textareas, and text measurement without DOM reflow.
Latest version: 0.3.1

# PretextUI

React components powered by [Pretext](https://github.com/chenglou/pretext) — virtual scrolling, auto-resize textareas, and text measurement without DOM reflow.

## Install

```bash
npm install @pretextui/core @pretextui/react @chenglou/pretext
```

## Quick Examples

**VirtualTextList** — virtual scroll 10,000 chat messages:

```tsx
import { VirtualTextList } from "@pretextui/react";

<VirtualTextList
  items={messages}
  renderItem={(item) => <div className="bubble">{item.text}</div>}
  defaultFont="16px Inter"
  lineHeight={22}
  height={600}
  padding={16}
  scrollToIndex={5000}
/>;
```

Width is measured automatically. Padding is subtracted from measurements internally.

**AutoTextarea** — grows with content:

```tsx
import { AutoTextarea } from "@pretextui/react";

<AutoTextarea
  font="16px Inter"
  lineHeight={24}
  minRows={1}
  maxRows={10}
  padding={16}
/>;
```

**BalancedText** — minimize ragged-right in headlines:

```tsx
import { BalancedText } from "@pretextui/react";

<BalancedText font="20px Inter" lineHeight={28} maxWidth={400}>
  The quick brown fox jumps over the lazy dog
</BalancedText>;
```

**useTextMeasure** — raw measurement hook:

```tsx
import { useTextMeasure } from "@pretextui/react";

const measurement = useTextMeasure(text, "16px Inter", 400, 24);
// returns { height, lineCount } or null while fonts load
```

**useRichTextMeasure** — measure mixed-font inline text:

```tsx
import { useRichTextMeasure } from "@pretextui/react";

const items = [
  { text: "Hello ", font: "700 16px Inter" },
  { text: "world", font: "16px Inter" },
];
const measurement = useRichTextMeasure(items, 400, 24);
// returns { height, lineCount } or null while fonts load
```

## API Reference

| Export               | Package | Description                                                                |
| -------------------- | ------- | -------------------------------------------------------------------------- |
| `VirtualTextList`    | react   | Virtualized list for variable-height text. Auto-measures container width.  |
| `AutoTextarea`       | react   | Textarea that auto-resizes with arithmetic height computation.             |
| `BalancedText`       | react   | Text block that minimizes ragged-right by computing optimal width.         |
| `useTextMeasure`     | react   | Hook returning text height and line count. Returns null during SSR.        |
| `useTextOverflow`    | react   | Hook detecting overflow and computing truncation index.                    |
| `useShrinkWrap`      | react   | Hook finding the minimum width that preserves line count.                  |
| `measureText`        | core    | Synchronous text height and line count measurement.                        |
| `measureTextLines`   | core    | Measurement with per-line text content and widths.                         |
| `findShrinkWidth`    | core    | Binary search for the tightest width preserving line count. Padding-aware. |
| `checkOverflow`      | core    | Check whether text overflows and find truncation point.                    |
| `createMeasureCache` | core    | Memoized measurement cache.                                                |
| `checkFont`          | core    | Check if a font string matches a loaded font.                              |
| `useRichTextMeasure` | react   | Hook measuring mixed-font inline text. Returns null during SSR.            |
| `measureRichText`    | core    | Measure inline-flow rich text height and line count.                       |
| `measureRichTextLines` | core  | Rich text measurement with per-line fragments and item indices.            |
| `findRichShrinkWidth` | core   | Binary search for tightest width preserving rich text line count.          |

## Gotchas

**Font must be loaded before measurement.** Measurements run after `document.fonts.ready`, but if a custom font never loads, results use a fallback. In development, PretextUI warns via `console.warn` when a font isn't ready.

**Font string format.** The `font` prop must be a valid CSS font shorthand: `'16px Inter'`, `'700 14px Arial'`. The family name must exactly match a loaded `@font-face` or system font.

**Width is auto-measured.** `VirtualTextList` measures its own container width via `ResizeObserver`. Pass an explicit `width` prop only to override. Items render after the first width measurement.

**Padding.** Pass `padding={16}` (symmetric) or `padding={{ left: 12, right: 20 }}` (asymmetric) so measurements account for container padding. Without this, text heights will be computed at the wrong width.

**SSR safe.** All hooks return `null` during server-side rendering. Components render without computed dimensions on the server and hydrate correctly on the client.

## How It Works

[Pretext](https://github.com/chenglou/pretext) computes text height through pure arithmetic — no DOM measurement, no layout reflow. PretextUI wraps this into React components that handle font loading, caching, container measurement, and lifecycle automatically.

## Performance

| Scenario             | Traditional (DOM)          | PretextUI                      |
| -------------------- | -------------------------- | ------------------------------ |
| 10,000 item heights  | ~500ms (reflow)            | ~19ms prepare + ~0.09ms layout |
| Textarea keystroke   | 1–5ms (hidden element)     | ~0.001ms (arithmetic)          |
| Scroll to item #5000 | Wrong position (estimated) | Pixel-perfect (exact)          |

`prepare()` runs once per unique text. `layout()` is the hot path.

## Changelog

See [CHANGELOG.md](CHANGELOG.md).

## License

MIT