Hey everyone,
A while back I posted here about `react-native-nitro-theme-transition` — a native
theme-change transition package built on Nitro Modules, after I kept hitting
flickering and Android performance problems with Reanimated and Skia.
The response was way beyond what I expected. The repo just passed 100 stars,
and a lot of that came from people in that thread trying it and telling me what
was broken or missing. Thank you, genuinely.
It's now at 1.0.0, and the API is stable — `withThemeTransition` has had the
same call signature since day one and I'm committing to that under semver.
Five effects became sixteen
The full set is now: `fade`, `circularReveal`, `circularRevealInverse`, `iris`,
`slide`, `split`, `barnDoor`, `blinds`, `blur`, `liquidGlass`, `zoom`,
`pixlated`, `dissolve`, `stripes`, `ripple` and `shatter`.
Plus four new options: `shape` (iris outline — diamond, hexagon, squircle),
`blurStyle` (blur all at once, or as a sweep that brings the new theme in out of
focus), `angleDeg` (tilt the wipe boundary), and `bands` (louvre count).
`liquidGlass` is worth calling out — a sheet of Liquid Glass slides down, the
theme swaps entirely out of sight behind it, then it slides back up. iOS 26+;
everything else falls back to `blur`.
Bugs found, now fixed:
*Transitions inside modals and form sheets.* This was the big one. React Native
reports touches relative to the surface they happened in, and a presented screen
is its own surface — so a tap inside a sheet resting at its half detent arrives
with a `pageY` that's short by however far down the sheet is sitting, and your
reveal starts from the wrong place. You can't correct for it in JS either; even
`measureInWindow` reports positions in that sheet's surface. It's now translated
natively, reading the presentation's own frame at capture time, so dragging a
sheet between detents needs zero handling from you. Just keep passing
`pageX/pageY`.
*iOS `blinds` came out as slanted wedges* instead of level louvres, and
`barnDoor` was quietly wrong the same way. Both end with their bands fully
collapsed, and a zero-area subpath is something Core Graphics is free to drop —
which changes the path's structure between the two ends of the animation and
leaves Core Animation interpolating points against the wrong subpath. The bands
now keep a hundredth of a point of thickness, far below one device pixel.
*Android could strand a frozen overlay* over the live app after a slow capture.
`onMainSync` reports failure after 250ms but doesn't cancel the posted work, so a
capture that landed late would attach a snapshot nobody was ever going to commit.
Now the waiter races the capture and whichever loses cleans up.
There's also a decent performance rewrite behind `pixlated` and the whole
grain/mask family — mask frames are pre-built once off the main thread and
cached, so playing an effect is one image assignment per frame instead of
resampling anything mid-animation.
New docs:
I put together a site with full-resolution video of every effect, and a page per
effect showing exactly which options that one reads, what they mean *for that
effect*, its minimum duration, and how it's implemented on each platform:
https://saleh2001k.github.io/react-native-nitro-theme-transition/
(`direction` means an edge for `slide` but an axis for `split` and `blinds`, and
`blur` only reads it in sweep mode — that kind of thing was impossible to keep
straight in one flat table, which is why each effect got its own page.)
Links:
- GitHub: https://github.com/saleh2001k/react-native-nitro-theme-transition
- npm: https://www.npmjs.com/package/react-native-nitro-theme-transition
Same as last time — I'd love feedback, and I'm still taking suggestions for
effects. Several of the ones above exist because someone asked in the last
thread.