Building a performative React Native app

Published: (January 8, 2026 at 01:02 PM EST)
7 min read
Source: Dev.to

Source: Dev.to

Hi there,

It’s 2026 and building an app with React Native could not become easier. Thanks to the open‑source React Native community and the Expo ecosystem, anyone can build an app faster than ever in the era of AI.

However, building a performant React Native app is still challenging. Your MVP might work, but it may not run smoothly across all mobile devices.

This blog covers the painful points on how to profile and build a performant React Native app that works as smoothly as possible—even on mid‑range Android devices.


Profile

Before you decide to improve performance, first understand what part of your app is really slow.

  • React Debugger – helps you spot expensive re‑renders, initial load time, and screen mount/unmount state.
    Don’t underestimate its value; it can dramatically improve performance.

React native flame graph

If you want to go beyond JavaScript, you can profile your app natively using Xcode or Android Studio. Native profiling lets you see:

  • Total memory usage
  • Memory consumption for a specific screen or operation
  • Native memory leaks
  • CPU usage

Native profiling is optional and can be skipped if your app doesn’t perform heavy operations (e.g., video rendering or 3D graphics).

You can use IDEs like Android Studio or Xcode to profile your app natively. By doing so, you’ll get a clear picture of overall memory usage, native leaks, and CPU load.

screenshot of Xcode

screenshot of Android Studio

At this point you should have a sense of which part of your app is slow. Now let’s discuss what and how to optimise it.


Rendering large lists

Rendering lists can be challenging. If you have simple items, use FlatList or FlashList (Shopify). Both are straightforward solutions for moderate lists.

The problem arises when each item takes a noticeable amount of time to render (e.g., ~400 ms). FlatList’s virtualization adds/removes items as the user scrolls, which can cause visible blanks because the CPU is busy performing virtualization.

Solutions

LibraryKey FeatureWhy it helps
FlashListItem recyclingReuses rendered items, reducing the cost of creating new ones.
LegendListReactivity & optional recyclingPrevents re‑rendering of items when nothing changes, eliminating blank spaces.
Legend‑StateFine‑grained reactivityWorks with LegendList for real‑time data updates in large, dynamic lists without unnecessary re‑renders.

LegendList is a robust solution for rendering large sets of items efficiently, accurately mitigating blank spaces through precise positioning and optional recycling. Pair it with Legend‑State for scenarios that require fine‑grained reactivity (e.g., live data feeds).


Initial boot time

Gif of Huddle01 app

Boot time is the period required for your app to load and run on a device. It includes loading and executing the JavaScript bundle in RAM.

If your app ships with a bloated JS bundle, users will experience a noticeable delay, leading to frustration.

Tips to reduce bundle size & launch time

  1. Enable Hermes (or another JS engine) for faster start‑up.
  2. Use code‑splitting (import() statements) to load screens lazily.
  3. Remove unused dependencies (npm prune, yarn autoclean).
  4. Enable Metro’s inlineRequires to defer module initialization.
  5. Compress assets (images, fonts) and use vector graphics where possible.
  6. Analyze the bundle with npx react-native-bundle-visualizer to spot large modules.

screenshot of JS bundler


Final thoughts

Performance optimisation is an iterative process:

  1. Profile → identify bottlenecks.
  2. Apply targeted fixes (list virtualization, bundle reduction, native profiling).
  3. Re‑profile to verify improvements.

By following the steps above, you’ll deliver a React Native app that feels snappy on both high‑end and mid‑range devices. Happy coding!

Optimising React‑Native Apps for Low‑End Android Devices


Bundle Size

  • Use tools like webpack‑bundle‑analyzer to see which parts of your bundle are the biggest contributors.
  • Identify heavy third‑party dependencies and replace them with lighter alternatives.

Note: Unlike the web, React Native does not support full‑blown code‑splitting out of the box.

  • You can try the Repack bundler, which adds code‑splitting support (requires extra configuration).

Best practice:

  • Keep media assets (images, SVGs, etc.) out of the JavaScript bundle.
  • Host them remotely and cache with libraries such as expo‑image.

Illusion of Lazy Loading

When building the Huddle01 Meet app we noticed a noticeable lag on Android devices. The cause? Rendering all bottom‑sheet components at once.

Solution

{showSheet && }
  • Render only the sheet that is currently visible.
  • Android devices (especially low‑mid‑range) have limited memory and CPU, so conditional rendering dramatically improves smoothness.

Rule of thumb: Don’t render UI that isn’t visible initially.
This technique is what I call “illusion of lazy loading.” Use it wisely and combine it with the Intersection Observer API (or its React‑Native equivalents) to avoid mounting off‑screen components altogether.


Synthetic‑Sugar Trade‑offs

  • Avoid “fancy” JavaScript libraries that only provide syntactic sugar.
  • Remember: JS accounts for ~80 % of a React‑Native app’s runtime, so every extra library can hurt performance.

Recommendations

  • Prefer custom implementations over heavy third‑party packages.
  • Skip CSS‑in‑JS libraries on low‑end Android devices unless they’re truly lightweight (e.g., react‑native‑unistyle).
  • Measure before you add any dependency – runtime overhead is the biggest enemy of performance.

Do not optimise for a feature that your target audience (e.g., $100‑range phones) will never use.


Imperative UI Updates

React’s declarative model works great, but sometimes a full re‑render is too costly.

  • setNativeProps ([setNativeProps](https://reactnative.dev/docs/legacy/direct-manipulation)) lets you mutate native views without triggering a React render.
  • Use it sparingly; it can desynchronise React state from the UI.

Typical use‑case: toggling dark/light mode on low‑end devices. Updating the entire UI via React state can cause noticeable lag; an imperative update may be faster, but you must keep the React state in sync manually.


Animations

  • react‑native‑reanimated offloads animation work to the UI thread, which is great for high‑end devices.
  • On low‑end Android phones the extra thread overhead can backfire, resulting in choppy or non‑functional animations.

Guideline

  • If your primary audience uses low‑end hardware, stick to vanilla React‑Native animations (Animated API) to minimise thread contention.

Leveraging GPU Resources

GPU Image from margelo.com

  • react‑native‑skia lets you draw graphics on the GPU directly from JavaScript, enabling real‑time image manipulation and shader effects.
  • react‑native‑filament (by Margelo) renders full 3D scenes on the native GPU – think “Pokemon GO‑style” experiences.

Why it matters: Off‑loading heavy visual work to the GPU reduces pressure on the JavaScript thread, leading to smoother UI on constrained devices.


C++ Magic & the New Architecture

React Native’s new architecture (TurboModules, Fabric, JSI) allows you to write performance‑critical code in C++ and expose it to JavaScript with virtually zero overhead.

  • Use TurboModules for native modules that need to be called frequently.
  • Leverage JSI (JavaScript Interface) to bridge C++ and JS without the bridge bottleneck.

Result: Faster native‑to‑JS communication, lower latency, and better utilisation of device resources.


Quick Checklist for Low‑End Android Optimisation

Action
1Analyse bundle size; prune heavy dependencies.
2Host media assets remotely; cache with expo-image.
3Render only visible components (conditional rendering, Intersection Observer).
4Avoid unnecessary “syntactic‑sugar” libraries; measure before adding.
5Use setNativeProps only when you can keep React state in sync.
6Prefer vanilla Animated API over Reanimated for low‑end devices.
7Off‑load graphics to GPU with react-native-skia or react-native-filament.
8Adopt the new architecture (TurboModules, Fabric, JSI) for C++‑level performance.

Bottom line:
Focus on bundle size, conditional rendering, minimal runtime overhead, and leveraging native GPU/CPU resources. With these strategies your React‑Native app will feel snappy even on the most modest Android phones.

React Native, JSI, and the Power of C++

The JSI (JavaScript Interface) eliminates the need for the traditional bridge, allowing direct access to native APIs from the JavaScript runtime. It is written in C++ and provides a high‑performance, low‑overhead way to call native code.

One of the most skilled C++ engineers and core contributors to React Native I know, Marc Mrousavy, and his team have published several powerful libraries that leverage JSI modules written entirely in C++.

  • react‑native‑quick‑crypto – a C++ implementation that does all the heavy lifting on the native side, keeping the JavaScript thread completely non‑blocking.
  • Nitro Modules – a new way of writing native modules that uses highly optimized JSI and is end‑to‑end type‑safe. Check the benchmark here.

Why C++ Matters Now

Thanks to AI‑assisted development, it’s becoming easier than ever to generate native modules based on specific requirements. Nitro Modules accelerate this process, enabling faster and more robust native code.

The line between native and JavaScript is blurring. C++ is the bridge that makes it straightforward to write custom native implementations, pushing the limits of what React Native can achieve.


Conclusion

The last five years have been a golden period for React Native. With the help of a supportive community, we now have a truly “write once, run anywhere” codebase. As we become more creative, we must push the limits of existing technology.

React Native may still have constraints in certain domains, but it will continue to improve. A future successor could eventually overcome the challenges we face today. It’s fascinating to watch cross‑platform technologies like React Native and Flutter evolve.

Thanks for reading this blog. Feel free to ask any questions in the comment section.

Happy New Year!

Back to Blog

Related posts

Read more »

Profiling with Ctrl-C (2024)

Article URL: https://yosefk.com/blog/profiling-with-ctrl-c.html Comments URL: https://news.ycombinator.com/item?id=46475296 Points: 12 Comments: 1...

Profiling with Ctrl-C

Article URL: https://yosefk.com/blog/profiling-with-ctrl-c.html Comments URL: https://news.ycombinator.com/item?id=46475296 Points: 6 Comments: 0...