Skip to content

intergalacticspacehighway/pretext-react-native-example

Repository files navigation

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

How it works

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:

Skia Example

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 .ttf via useFont

Note: Skia's font.measureText() returns the bounding box, not the advance width. Using font.getGlyphIDs() + font.getGlyphWidths() gives correct advance widths that match Skia's drawText positioning.

Native Example

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)

Setup

npm install
npx expo prebuild
npx expo run:ios
npx expo run:android

Requires a dev client (native modules can't run in Expo Go).

Polyfill

Hermes doesn't have Intl.Segmenter. The app loads @formatjs/intl-segmenter/polyfill-force before importing pretext:

import "@formatjs/intl-segmenter/polyfill-force";

Key findings

  • iOS exclusionPaths: iOS natively supports text wrapping around shapes via exclusionPaths. A single UITextView with 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 single UITextView with exclusionPaths (iOS) or StaticLayout.setIndents (Android). See this implementation.
  • Skia measureText().width returns the bounding box width, not the advance width. Use getGlyphWidths() for layout.
  • Font matching: When using platform/system fonts, use native measurement (NSString.size on iOS, Paint.measureText on Android). When using custom fonts loaded via Skia, use Skia's getGlyphWidths(). Never mix measurement and rendering engines, they could potentially disagree on sub-pixel widths.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors