Benchmarks
Reproducible web and native performance comparisons
Uniwind was fastest in all 15 native cells. Tamagui Tailwind was second in all 15. On web, Tailwind CSS was fastest in 10 cells, Tamagui Tailwind in three, and inline React in two.
Green cells mark the lowest mean in each row. All values are mean milliseconds. Lower is faster.
Web results
The web fixture renders 200 items, or 60 in the heavy case. Mount starts with an
empty root. Update changes fixture state without replacing the root. Remount
replaces the tree. Timing starts before the React state change and ends in
useLayoutEffect.
Each production build ran in Chromium 151.0.7922.34 on an Apple M5 Pro with 18
logical CPUs and 64 GiB of memory. The runner used Bun 1.3.14 on Darwin
25.5.0. Two warmup rounds preceded 10 retained rounds. Seed 73129 shuffled
every framework and scenario round. All 50 behavior checks passed before timing.
Simple, rich, group, and heavy use Tamagui 3.0.0-beta.901.1. Animated uses
Tamagui 3.0.0-beta.917.1. The animated compiler flattened all 14 candidates
with zero bailouts. The other versions are Tailwind CSS 4.3.0, NativeWind
5.0.0-preview.4, React Native Web 0.21.2, Uniwind 1.11.0, React 19.2.3,
and React DOM 19.2.3.
Tamagui Tailwind mounted faster than NativeWind and Uniwind in all five web scenarios. The paired 95% confidence intervals favored Tamagui in each of those 10 comparisons. Tailwind CSS had lower means than Tamagui in 10 of 15 cells. Inline React had the lowest simple and group update means.
The Tamagui arm uses @tamagui/tailwind. This table does not measure Tamagui’s
component APIs.
Native results
All three Release apps render the same native-class-bench.tsx fixture. They use
the same class strings, component tree, item counts, state changes, and timing
boundary. Simple, themed, rich, and group render 200 items. Heavy renders 60
three-component groups.
The fixture uses explicit point dimensions. An earlier smoke run found that
w-5 resolved to 52 points under the benchmark’s Tamagui config and 20 points
in the other arms. That fixture was rejected. Fixture version 2 passed an
independent 20 by 20 layout check in all three apps before timing.
The campaign used Tamagui Tailwind 3.0.0-beta.917.1, NativeWind
5.0.0-preview.4, and Uniwind 1.11.0. NativeWind also used
react-native-css 3.0.7, Reanimated 4.5.1, and Worklets 0.10.1. Every app
used Expo 57.0.15, React 19.2.3, and React Native 0.86.2.
The apps ran on an iOS 26.4 simulator on an Apple M5 Pro with 18 logical CPUs
and 64 GiB of memory. The runner used Bun 1.4.0, Node 26.3.0, and
XcodeBuildMCP 2.3.0 on Darwin 25.5.0. It waited until three host samples were
71.8%, 63.4%, and 74.1% idle. Two warmup rounds preceded 12 retained
rounds. Seed 73129 shuffled every round.
Uniwind had the lowest mean in every native cell. Its paired 95% confidence intervals also favored it over Tamagui in all 15 cells. Tamagui had lower means than NativeWind in all 15 cells. Several Tamagui and NativeWind paired intervals overlapped zero because NativeWind’s observations varied widely.
Source record
The cross-framework harness began in code/comparisons. It was extracted from
this monorepo in commit 300a4db8de
so NativeWind does not enter the main workspace dependency graph.
The web values come from committed receipts 8b45f79809 and 9896f9c488. The
native values come from fixture source commit 84fc84bb5d and receipt commit
352925a2ac. The native receipt contains all 180
retained observations, 30 warmups, confidence intervals, package versions,
fixture hashes, bundle hashes, build identities, and behavior signatures. The
comparison repository does not have a public remote yet. The web table is also
retained in the Tamagui Tailwind post.
The monorepo still contains Tamagui’s conformance harness, runtime microbenchmarks, compiler graph benchmark, bundle checks, and browser fixture.
What this does not measure
- The web run covers production rendering after load. It does not measure server rendering, hydration, interaction latency, or network cost.
- The native run uses one simulator. It does not measure a physical device, Android, startup, memory, GPU work, or energy use.
- The timers end in
useLayoutEffect. They include React reconciliation and the synchronous native commit. They do not include the next fully drawn frame. - The behavior gate checks this fixture. It does not prove support or visual equivalence for every utility.
- Compiler acceptance and rendered pixels are separate questions.
- Small differences can move with hardware, operating system, library version, and application shape.
- Bundle size and build time are tracked separately. Each library ships a different mix of runtime and build tooling.