React Native Performance

Users judge mobile apps by feel: 60 (or 120) frames per second scrolling, instant responses to taps, and quick launches. A React Native app has two main places where performance goes wrong. The JavaScript thread gets busy (too many re-renders, heavy computation, large JSON parsing), which delays responses to touches. The UI thread has too much to draw (huge lists, unoptimized images, expensive layouts and animations).

Most issues have well-known fixes: virtualized lists (FlashList or tuned FlatList), avoiding unnecessary re-renders, running animations on the UI thread with Reanimated, optimizing images, and trimming startup work. As always, measure before optimizing, on real devices, in release builds.

TL;DR

Quick Example

A performant product list:

Core Concepts

Measuring

Always measure release builds: development mode adds checks and runs slower.

JS Thread vs UI Thread

With the New Architecture, communication is cheaper, but a busy JS thread still delays responses to user input.

Lists

Long lists are the most common performance problem.

Avoiding Unnecessary Re-renders

The same principles as React performance:

Animations and Gestures

Animations driven from JavaScript stutter whenever the JS thread is busy. Reanimated runs animation logic as worklets on the UI thread via JSI, and react-native-gesture-handler processes gestures natively. Combined, they keep interactions at full frame rate even while JS is working. Prefer animating transform and opacity, which are cheap, over layout properties.

Images

Startup Time

Memory

Watch for leaked listeners and timers (clean up in effects), large caches held in JS, and images kept alive. Use profilers to compare heap snapshots before and after navigating screens repeatedly.

Best Practices

Test on Low-End Android Devices

Mid- and low-range Android phones are far slower than flagship iPhones. If it's smooth there, it's smooth everywhere. Include them in QA and performance testing.

Set Performance Budgets

Track startup time, frame rates on key screens, and bundle size in CI or release checks, with automated measurement where possible, so regressions are caught early.

Move Work Off the JS Thread

Heavy computation (parsing large data, image processing, crypto) belongs in native modules, worklets, or the backend. Paginate and pre-process data server-side.

Keep Dependencies Lean

Every library adds bundle size and startup cost. Prefer lightweight, maintained libraries, and remove unused ones.

Common Mistakes

Inline renderItem and Unstable Props

New functions on every render defeat memoization of rows. Define stable callbacks, and memoize row components (or rely on the React Compiler).

console.log in Production

Logging large objects in hot paths is surprisingly expensive. Strip console statements in release builds (a Babel plugin), and use a proper logger with levels.

Animating Layout From JavaScript

Updating width or top via state on every frame forces re-renders and layout recalculation on the JS thread. Use Reanimated with transforms instead.

FAQ

Why is my React Native app slow in development?

Development builds include extra validation, debugging hooks, and unoptimized JavaScript, so they're significantly slower than release builds. Always evaluate performance in a release build on a real device.

FlatList or FlashList?

FlashList for most long or complex lists: its view recycling gives smoother scrolling and lower memory use, especially on Android. FlatList is fine for short lists, or when you need a feature FlashList lacks, but tune its windowing props.

How do I make animations smooth?

Run them on the UI thread with Reanimated (shared values, worklets, layout animations) and handle gestures with react-native-gesture-handler. Animate transforms and opacity rather than layout properties, and avoid triggering React re-renders on every frame.

How can I reduce app startup time?

Use Hermes, lazy-load screens and heavy dependencies, defer non-essential initialization until after the first render, use fast synchronous storage like MMKV sparingly, and keep bundle size small. Measure cold start times on real devices to verify gains.

Related Topics

References