DEV Community

Cover image for Unlocking 60FPS: My Deep Dive into React Native Performance (Expo vs. Bare Workflow
Bernard Emmanuel
Bernard Emmanuel

Posted on

Unlocking 60FPS: My Deep Dive into React Native Performance (Expo vs. Bare Workflow

Why your React Native app still stutters 🐌 (and why I’m leaning harder into Expo)

I’ve been building with React Native for a while now, and we’ve all hit that specific wall: the app logic works perfectly, but the moment you add a heavy animation or a large list, the UI feels "sticky." 😩

I spent the last week going down a rabbit hole trying to debug a janky scroll interaction. In the process, I went through Evan Bacon’s deep dives on performance and watched a breakdown by Beto on the Expo channel.

It completely changed how I look at the "Bare" vs. "Expo" debate. Here are my raw notes on why performance optimization has shifted recently. 👇

The Bottleneck is still the JS Thread 🧵
Before looking at tools, you have to respect the architecture. As Beto visualized really well, we are still largely fighting a war on two fronts: the UI Thread (painters 🎨) and the JS Thread (managers 👔).

The problem is that the JS thread is a micro-manager. It tells the UI thread what to do. If your JS thread is busy parsing a massive JSON response or calculating Fibonacci sequences, it stops talking to the UI.

The UI assumes there’s nothing to do, so it freezes. 🥶

The React Compiler: "It just works" vs. Babel Hell 🔥
The React Compiler is probably the biggest shift we've seen in a while. It basically automates memoization so you don't have to spam useMemo everywhere.

In a Bare Workflow: I tried setting this up in an older bare project recently. It’s powerful, but you have full responsibility for the build pipeline. If you mess up the Babel config, you’re on your own. I spent two hours just debugging syntax errors before I even saw a performance gain. 🫠

In Expo (SDK 54+): This is where I started getting jealous. In the newer SDKs, the compiler is often enabled by default or requires a single config line. Expo abstracts the Babel mess. You update the SDK, and the app runs tighter. It feels less like configuration and more like a free upgrade. 🚀

Multithreading and Worklets ⚡
Sometimes optimizing the JS code isn't enough; you need to get off the main thread entirely. This is where Worklets come in.

In a bare app, using C++ TurboModules or Worklets often means touching native code—specifically android/build.gradle or the dreaded iOS Podfile. If a build fails here, you aren't debugging JavaScript anymore; you're debugging Xcode logs. 💀

With Expo, the native runtime is managed. You generally just npx expo install the library, and because the native code is pre-linked, you can just write the JS and let the math happen on a background thread.

A Debugging Tip 🕵️‍♂️
Regardless of your stack, you can't fix what you aren't measuring.

One thing that genuinely helped me find my bottleneck was a tip from the Beto video: open the React Native DevTools (press j in the terminal) and enable "Highlight updates when components render."

I thought my list was optimized, but the moment I typed into a search bar, the entire screen flashed. 💥 I was re-rendering the whole page on every keystroke. No amount of native code would have fixed that logic error.

The Takeaway
I used to be a "pure native" snob, but honestly, the Expo team is baking best practices into the framework faster than I can implement them manually.

If you’re still managing your own native projects, just know that you are signing up to maintain your own Babel configs and Podfiles. If you want to focus on the JavaScript performance—which is usually where the lag actually comes from—the managed workflow is just winning right now. 🏆

Has anyone else tried the new React Compiler defaults in SDK 54 yet? I'm curious if you saw a frame rate jump without changing your code. Let me know below! 👇

Top comments (0)