Benchmarks
Measured on two slower phones, on purpose: an iPhone 11 Pro (60 Hz) and a Samsung Galaxy A22 (90 Hz, a low-end MediaTek phone). Release builds, React Native 0.87.1. The bench app in the repository (bench/) is launched with a plan by scripts/bench/run.mjs and measures every frame on both threads with a native probe. Hover a bar for every figure of its row. The full tables, the method and the memory measurements are in BENCHMARKS.md, and every number comes from a result file in scripts/bench/results/.
NitroNumber
NitroNumber is measured with both of its transitions: the roll (the value prop, and jumpTo, which positions the wheels continuously) and the numeric transition, which swaps each changed digit in place and draws more per frame. The Roll / Numeric switch on each chart picks which one the chart compares with the other libraries.
24 numbers, a new value every frame. UI is the main thread's frame rate and the frames it missed; JS is the frame rate the JS thread still managed. On the iPhone 11 Pro every Nitro path, the roll and the numeric transition alike, holds 60 fps with nothing dropped, jumpTo on under a quarter of one core. On the Galaxy A22, where even plain <Text> manages 81 of 90, jumpTo matches it and keeps the JS thread at 76 fps; the value prop and the numeric transition, which re-render 24 components a frame, hold 72–74. The other libraries drop frames, starve the JS thread, or both: on the 11 Pro animated-rolling-numbers keeps the UI at 59 fps with the JS thread at 11, which animates but does not respond.
value propjumpToA list. 200 rows in a FlatList, scrolled back and forth while ten values a second arrive in every row, rows mounting as they scroll in. The roll keeps the list at 60 fps with nothing dropped on the 11 Pro; the numeric transition, which draws more for every changed digit, drops to 56. On the A22 jumpTo holds 79 fps and the value prop 67 against plain text's 59, because a re-rendered <Text> is re-measured on every update. NumberBloom holds 84 there with its JS thread at 1 fps; the other libraries fall below, NumberFlow View to under one frame a second on the 11 Pro.
value propjumpToMounting 24. This is where a native view pays for being native: 28–34 ms on the 11 Pro against 17 ms for plain text, 14–15 ms of it on the main thread, once; 158–172 ms against 99 on the A22. That is below the other native library and a fraction of the Reanimated digit trees, which take 0.2–0.4 s on the 11 Pro and up to 2 s on the A22.
value propjumpToMemory. 24 copies mounted and unmounted 40 times in a fresh process, the footprint read as a floor after a forced collection, growth per cycle. The Nitro number ends where it started on both phones, roll and numeric alike, within the few megabytes plain text moves by, and on the Galaxy A22 the live View count stays flat. The digit-tree libraries grow by 1–23 MB a cycle.
value propjumpToNitroInput
Typing at eight keys a second. The figure to read first is rewritten keys: a field that formats in JavaScript (TextInput with onChangeText → Intl.NumberFormat → value, and the libraries built on that pattern) shows the raw keystroke first and the formatted text a frame or two later, 10 keys in 12. NitroInput formats inside the native edit, plain or reflowing, and never rewrites a key; it costs 1–5 ms of JavaScript per key where the JS-formatted fields cost 11–43. The reflow adds its animation frames and about 10 ms (11 Pro) to 17 ms (A22) of JavaScript per key: the field grows with its text, and React Native lays the new width out.
Focus. focus() to onFocus: 6–10 ms on the 11 Pro and under 2 ms on the A22, where TextInput takes 51 and 21 ms, because the call goes straight into the native view instead of through a Fabric view command and the batched event emitter.
Mounting 20 fields. A Nitro view costs more to create than a TextInput: 35–73 ms against 26 on the 11 Pro, 213–296 against 144 on the A22. Unmounting costs about the same for every field.
NitroText
1000 one-line labels ("Label number 1" to "Label number 1000", 16 pt) mounted at once, against react-native-plain-text's PlainText and React Native's Text. The bench app's text screen runs by itself when launched with {"textMount":{"rounds":N}}: each round mounts the three in turn and reports the JS slice, the main thread's CPU over the next second and its longest frame. The first round creates the views; the figures below are the median of the later rounds, which mount into views Fabric reuses and which last showed other text.
| iPhone 11 Pro main thread | commit | longest frame | Galaxy A22 main thread | commit | |
|---|---|---|---|---|---|
NitroText | 113 ms | 66 ms | 50 ms | 510 ms | 192 ms |
PlainText | 112 ms | 73 ms | 55 ms | 620 ms | 214 ms |
Text | 140 ms | 126 ms | 107 ms | 790 ms | 319 ms |
A reused view that already shows the same text draws nothing, as a UILabel does: then it is 54 ms on the iPhone, against 50 for PlainText and 149 for Text. The A22 spreads a mount over several frames, so its longest frame says little and is left out.
NumberFormat
Per call on the JS thread, against Hermes' Intl.NumberFormat, six locale and currency setups. Details and the method are on the NumberFormat page.
| iPhone 11 Pro, Hermes | iPhone 11 Pro, NumberFormat | Galaxy A22, Hermes | Galaxy A22, NumberFormat | |
|---|---|---|---|---|
| Building a formatter | 42 µs | 4.9 µs | 3,319 µs | 9.2 µs |
format() | 1.6 µs | 1.2 µs | 10.0 µs | 2.8 µs |
formatToParts() | not implemented | 3.4 µs | 93 µs | 7.6 µs |
A formatter per call (toLocaleString) | 85 µs | 6.2 µs | 2,035 µs | 11 µs |