Performance work in React Native pays off only when it is driven by measurement. Most slow screens come from a handful of repeatable causes: oversized lists, unnecessary re-renders, heavy JavaScript on the main interaction path, and unoptimized images. Below is the order I work through them in production apps.
Key takeaways
- Measure startup, rendering, memory, and network behavior before changing code.
- Use virtualization, memoization, and stable component boundaries where profiling proves they help.
- Track performance in production because development builds do not represent real user conditions.
Measure before you optimize
Always start from a profiling session, not a guess. Use the in-app performance overlay and Flipper/Hermes profiler to capture frame drops, then record a CPU profile of the slow interaction. Development builds run unminified JavaScript and skip Hermes optimizations, so confirm every number on a release build before drawing conclusions.
Define what "fast enough" means for the screen: a target cold-start time, a 60fps scroll, and an acceptable time-to-interactive. Without a target you cannot tell when to stop.
Fix the common bottlenecks
Lists are the most frequent offender. Replace ScrollView-with-map patterns with FlatList or FlashList, give every row a stable key, set realistic getItemLayout where possible, and tune windowSize and maxToRenderPerBatch for the content.
Re-renders are next. Wrap pure leaf components in React.memo, stabilize callbacks and derived data with useCallback/useMemo, and keep state as local as possible so a single update does not cascade through the tree. Move expensive work off the JS thread with InteractionManager or a native module when it blocks gestures.
Finally, ship Hermes, enable inline requires and the RAM bundle where it helps cold start, and compress and correctly size images instead of scaling large assets at runtime.