An Expo app demonstrating pretext running on React Native, text wrapping around a draggable shape, powered by pretext's line-breaking with pluggable measurement function.
Screen.Recording.2026-03-29.at.9.35.51.PM.mov
This project ports it to React Native by injecting a custom setMeasureFunction, pretext's line-breaking algorithm runs unchanged, only the measurement function is swapped. Checkout this PR for more information.
Two examples are included:
Uses @shopify/react-native-skia for both measurement and rendering.
- Measurement:
font.getGlyphWidths()(advance widths, not bounding box) - Rendering: Skia
<Canvas>+<Text> - Font: Roboto loaded from
.ttfviauseFont
Note: Skia's
font.measureText()returns the bounding box, not the advance width. Usingfont.getGlyphIDs()+font.getGlyphWidths()gives correct advance widths that match Skia'sdrawTextpositioning.
Uses a custom Expo Module for measurement and standard RN <Text> for rendering.
- Measurement:
NSString.size(withAttributes:)on iOS,Paint.measureText()on Android - Rendering: Absolute-positioned RN
<Text>components - Font: System font (San Francisco on iOS, Roboto on Android)
npm install
npx expo prebuild
npx expo run:ios
npx expo run:androidRequires a dev client (native modules can't run in Expo Go).
Hermes doesn't have Intl.Segmenter. The app loads @formatjs/intl-segmenter/polyfill-force before importing pretext:
import "@formatjs/intl-segmenter/polyfill-force";- iOS
exclusionPaths: iOS natively supports text wrapping around shapes via exclusionPaths. A singleUITextViewwith an exclusion path handles line breaking, rendering, selection, and accessibility, no manual layout needed. Android has no direct equivalent.StaticLayout.Builder.setIndents()accepts per-line left/right indent arrays, but only supports shapes at the edges, not both sides simultaneously. - Text selection: Skia Text does not support native text selection. Absolute-positioned RN
<Text>lines support per-line selection but not cross-line drag selection. For full native text selection with copy/paste, consider a singleUITextViewwithexclusionPaths(iOS) orStaticLayout.setIndents(Android). See this implementation. - Skia
measureText().widthreturns the bounding box width, not the advance width. UsegetGlyphWidths()for layout. - Font matching: When using platform/system fonts, use native measurement (
NSString.sizeon iOS,Paint.measureTexton Android). When using custom fonts loaded via Skia, use Skia'sgetGlyphWidths(). Never mix measurement and rendering engines, they could potentially disagree on sub-pixel widths.