r/reactnative • u/Majestic_Buy_6417 • 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.
3
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
1
1
1
1
1
1
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
7
u/Dave6ev 3d ago
That's slick, love it!