React Native port of pretext — measure text height and line breaks before rendering.
In React Native, you can't know a text component's height until after it renders. The common workarounds are:
onLayout/onTextLayout— fires after render, causing layout jumps- Hidden render pass — renders offscreen first just to measure, wasting a frame
- Hardcoded estimates — breaks with dynamic content, different fonts, or locales
This makes FlatList's getItemLayout impossible to implement correctly and causes visible UI flicker.
pretext-native talks directly to each platform's native text engine (iOS CoreText, Android StaticLayout) via JSI — no rendering required. A two-tier LRU cache delivers 95%+ hit rate and 2–5M ops/s on warm lookups.
Traditional (onLayout):
Render (height unknown) → onLayout fires → Re-render with correct height
= 2 render passes, visible layout jump
pretext-native:
Measure → Render (height known)
= 1 render pass, no jump
Fully compatible with React Native's New Architecture (Fabric + TurboModule).
yarn add pretext-native
# iOS only
cd ios && pod installimport { useTextLayout } from 'pretext-native'
const { height, lineCount, isTruncated } = useTextLayout({
text,
width: 320,
fontSize: 15,
lineHeight: 22,
maxLines: 3,
})onLayout |
react-native-text-size |
pretext-native | |
|---|---|---|---|
| Pre-render measurement | No | Yes | Yes |
| Synchronous API (JSI) | No | No | Yes |
getItemLayout support |
No | Manual | Built-in |
| Built-in cache | N/A | No | Yes (95%+ hit rate) |
allowFontScaling |
N/A | Yes | Yes |
| Custom font validation | N/A | No | Yes (isFontAvailable) |
| TurboModule (New Arch) | N/A | No | Yes |
| Package size | 0 (built-in) | 167KB | 13KB (core) + 120KB |
| Runtime dependencies | N/A | 0 | 0 |
| Scenario | Cold | Warm (cached) |
|---|---|---|
| Short text (13 chars) | 815K ops/s | 5.8M ops/s |
| Medium text (180 chars) | 112K ops/s | 2.3M ops/s |
| Long text (1.2K chars) | 17K ops/s | 503K ops/s |
| CJK text (120 chars) | 159K ops/s | 2.7M ops/s |
// React hook — sync via JSI, async fallback
useTextLayout(options): UseTextLayoutResult
// Direct measurement
measureTextSync(input): TextMeasureResult
measureText(input): Promise<TextMeasureResult>
measureTextBatch(inputs): Promise<TextMeasureResult[]>
// Cache management
prewarmCache(inputs): Promise<void>
clearCache(): void
// Utilities
isNativeAvailable(): boolean
isFontAvailable(fontFamily): boolean- FlatList
getItemLayout— pre-calculate item heights for smooth scrolling - Chat bubbles — single-line width optimization and message list pre-warming
- Truncation / "Show More" — detect overflow without rendering
The example app includes 6 demo screens: basic measurement, dynamic width, speed benchmark, FlatList integration, chat bubbles, and "Show More" truncation.
cd example && yarn install
cd ios && bundle install && bundle exec pod install && cd ..
yarn ios # or yarn android- Measures single-style text blocks only (no mixed inline styles or inline images)
- JS fallback is heuristic-based, not pixel-perfect — use native modules for production accuracy
- API is at v0.0.2 — core APIs are stable, secondary APIs may evolve before v1.0
- iOS 13+
- Android API 21+
- React Native 0.71+ (New Architecture & Legacy Bridge)
Contributions are welcome! Please read the Contributing Guide before submitting a pull request.