Watch who has to re-render.
Both phones run the same Account screen. On the left, every styled component renders again, and each one flashes as it does. On the right, shadewind writes the new props in one native commit, so nothing flashes and the JS thread stays flat.
setTheme('dark')
tap to switch · illustration, not a measurement
Up and running in three files.
Your existing classNames don't change. Pick your setup, then walk the steps.
Props per theme, written in at commit time.
Shadewind remembers each view's props for every theme and rewrites them inside React Native's own shadow tree commit.
setTheme('dark')
A native commit hook picks it up
Each bound view's dark props are written in
Mounted in the same commit
Animated switch, optional
setTheme('dark', { transition: { type: 'fade', durationMs: 250 } }) cross-fades. Instant by default.
Precompile, optional
precompileClassNames: true turns classNames on View and Text into hookless components to cut mount cost.
See what it binds
A dev-only diagnostics log lists every view it binds and every commit it themes.
Tested natively
A harness runs React Native's real C++ renderer, so commit logic is checked without a device.
Honest fallbacks
Inline backgrounds over a className colour and active: states re-render, as in plain Uniwind.
Bounded memory
Since 0.3.2 the style cache is bounded, so thousands of new classNames don't grow the process.
Against plain StyleSheet, the floor.
StyleSheet is React Native with no styling library, the fastest anything can mount. Time is measured until the JS thread is idle, ten runs each. These are simulator and emulator numbers; nothing has been measured on a physical device yet.
JS-thread time; zero components re-render. Colours reach the screen about 57 ms (iOS) and 47 ms (Android) after the call, and that main-thread mount is React Native's own work. A StyleSheet app has no runtime theme switch to time, so there is no baseline lane here.
The example-real soak mounts and unmounts about 1,200 views per cycle, 200 of them with a className never seen before. Each reading is the process footprint after two forced GCs. 0.3.2 bounds the style cache. Its lower second reading is warm-up, not shrinkage, and mount time did not change. A normal app with a fixed set of classNames did not grow in any build.
Full protocol, spreads and what didn't help: docs/BENCHMARK.md
Experimental, and upfront about it.
0.x · New Architecture only
React Native 0.86. iOS is the primary target; Android has one emulator of testing so far. Verified with Uniwind 1.12.x, and 1.13.x without the native class resolver. Other versions run as plain Uniwind.
From the author
Vibe-coded with Claude Code by a dev who loves programming. Take the code and numbers with a grain of salt, and kindly roast where it's wrong.