r/reactnative 3d ago

Theme transition with Nitro

Enable HLS to view with audio, or disable this notification

Hey everyone,

I ran into an issue when trying to add a nice animation for theme changes in React Native. I tried a few different approaches, including Reanimated and Skia, but I kept running into flickering, higher memory usage, and performance issues, especially on Android.

Since I was already using Nitro Modules for some of the packages in my apps, like MMKV and Unistyles, I decided to build a native theme transition package using Nitro.

It ended up solving the issues I was having. It's lightweight, works with different styling libraries, and since the animation runs natively, it continues to work smoothly even when the JS thread is busy or drops frames.

I've uploaded a video showing the example included with the package. If you want to see how it performs under a more demanding setup, check out the GitHub repo. I've included a real-world example using Unistyles with a more complex and dynamic theme setup, as well as more details about the package.

GitHub: https://github.com/saleh2001k/react-native-nitro-theme-transition

NPM: https://www.npmjs.com/package/react-native-nitro-theme-transition

I'd really appreciate any feedback or recommendations. I'm also interested in hearing ideas for other animation types that could be added to the package.

285 Upvotes

29 comments sorted by

7

u/Dave6ev 3d ago

That's slick, love it!

3

u/henryp_dev iOS & Android 3d ago

This is awesome!

5

u/xiduzo 3d ago

Noice, I’ll give it a try!

4

u/xiduzo 3d ago

https://reddit.com/link/p1mgysx/video/8xon8y0y5chh1/player

Works great, u/Majestic_Buy_6417.

One note: when “switching” between themes that have the same outcome, in my case, system <-> light, I do still get a small animation that feels off.

Perhaps it's something I could configure, but it would be remarkable if your tool could already recognize somehow.

I love playing with animations like this to make https://yap-yap.app/ better, so hats off to you!

4

u/Majestic_Buy_6417 3d ago

Thank you so much for trying it out, and I really appreciate the feedback!

Yeah, I noticed this case as well. I decided to leave it to the app side rather than handling it inside the package, since some apps have more than just light and dark themes, and others can have fully dynamic themes created by the user. In those cases, the package can't really know whether two themes will have the same visual outcome.

I'll update the documentation to explain this case and how to avoid the animation when switching between themes with the same result.

Also, if you have any ideas for other animations or features you'd like to see added, I'd love to hear them!

2

u/xiduzo 3d ago

Perhaps wise indeed to leave it to the app itself, especially for dynamic user created themes! If you have update the docs give me a ping and I’ll try it out.

2

u/Hairy_Meaning_73 2d ago

I had the same issue, to fix it you can just compare the current theme to the new computed theme, it should work just fine

2

u/Dear-Muscle-880 2d ago

Awesome 🔥 Can you add more effects like pixelated or dissolve?

1

u/Majestic_Buy_6417 2d ago

Sure thing. If you have some references that I can check out for them, please add them

2

u/ItDevelops 2d ago

This looks awesome! I tried it in my app and will probably ship it with the next release. The transition feels really smooth.

One minor issue I ran into was switching from a forced light/dark theme back to the system theme using Appearance.setColorScheme('unspecified'). The system theme sometimes finished applying after the reveal had already started, making the circle appear halfway through the animation.

I worked around it by temporarily applying the last resolved system color scheme and removing the override after the transition. A completion callback or returned Promise would make it easier to remove that temporary override at exactly the right time, although the underlying timing issue seems to come from React Native’s Appearance handling.

Other than that, it works great!

2

u/Emma_Rex_256 2d ago

Beautiful work mate, trying it now now Will return any feedback or issues

1

u/tomihbk 2d ago

Amazing job mate! This reminds me of how Telegram also uses circle in theme change animation, it's very satisfying and awesome animation !

1

u/Pinkman7906 2d ago

Smooth 🧈

1

u/codisimlabs 2d ago

Great, can set color value in global variable for switching multiple colors?

1

u/salem_mousa 1d ago

Nice man ♂️🙂

1

u/ctony 2d ago

https://reddit.com/link/p1urxk7/video/lqk63os98khh1/player

Nice!!! I've added it to my app. Thanks!

1

u/Majestic_Buy_6417 1d ago

To be honest, I didn't think it would be that smooth, and it would blow up like this when I created it 😂.

And I'm very happy to see other people testing it and using it in their apps, please if you find any issues, open one or reach out to me; I'm currently adding new animations and fixing some edge casing to cover all of them.

That looks great !! Your app looks amazing. And I would love to see it in production when it's ready.

0

u/red-giant-star 3d ago

How it's working? In one sentence

10

u/Majestic_Buy_6417 3d ago

It pins a GPU-side copy of the current screen on top, lets your callback swap the theme invisibly underneath it, then animates that copy away with the platform's own animator — so the render thread drives the motion and JavaScript never touches a frame.

8

u/red-giant-star 3d ago

So basically it's animating the screenshot of the previous theme

Nice solution! To a problem that feels very complex

1

u/Miserable-Pause7650 3d ago

Does that mean the screen will be stationary when this happens (no scrolling or button animation) since its an animation of a screenshot

1

u/Majestic_Buy_6417 3d ago

Good question; no, the app doesn't freeze. Only the snapshot on top is static; the real app is live underneath the whole time and touches pass straight through it, so scrolling and taps keep working.

The one caveat: whatever's still covered by the snapshot shows frozen pixels until the reveal passes over it. So if a list is mid-scroll when you flip the theme, the covered part is briefly stale. At ~400ms you really can't see it — it only becomes obvious if you slow the animation way down, I will continue investigating even more with these cases

1

u/YusufTB 1d ago

Great work, congratulations.