React Native Best Practices

Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Applies to tasks

What Is This?

Overview

React Native Best Practices is a skill that provides structured performance optimization guidelines for mobile applications built with React Native. It covers critical performance dimensions including frames per second (FPS), time to interactive (TTI), bundle size reduction, memory leak prevention, unnecessary re-render elimination, and smooth animation delivery. The skill draws from Callstack's deep expertise in the React Native ecosystem and applies directly to real-world development challenges.

The skill is particularly focused on the technical layers where performance problems originate: the JavaScript thread, the native bridge, the Hermes engine, and the rendering pipeline. Rather than offering generic advice, it provides actionable patterns and diagnostic approaches that developers can apply immediately to production codebases.

Whether you are building a new application from scratch or optimizing an existing one that suffers from jank, slow startup, or excessive memory consumption, this skill gives you a systematic framework for identifying and resolving bottlenecks at every layer of the stack.

Who Should Use This

  • Mobile developers building or maintaining React Native or Expo applications who need to meet performance benchmarks
  • Frontend engineers transitioning from web development who are unfamiliar with mobile-specific performance constraints
  • Technical leads responsible for setting performance standards and code review guidelines across a React Native team
  • Performance engineers tasked with profiling and debugging frame drops, slow TTI, or memory leaks in production apps
  • Developers integrating native modules or third-party libraries who need to manage bridge overhead carefully
  • Architects designing component hierarchies and state management strategies for large-scale React Native applications

Why Use It?

Problems It Solves

  • JS thread blocking: Identifies patterns that saturate the JavaScript thread and cause dropped frames, providing techniques to offload work using InteractionManager, worklets, or native modules.
  • Bridge overhead: Guides developers in reducing serialization costs when communicating between JavaScript and native layers, especially relevant before the New Architecture is fully adopted.
  • Unnecessary re-renders: Provides strategies for auditing and eliminating component re-renders caused by unstable references, poor memoization, or inefficient context usage.
  • Large bundle sizes: Offers techniques for tree shaking, lazy loading, and analyzing bundle composition to reduce TTI and initial load time.

Core Highlights

  • Hermes engine optimization patterns for faster startup and lower memory usage
  • FlashList integration guidance as a high-performance replacement for FlatList
  • Animation best practices using Reanimated 2 worklets to keep animations on the UI thread
  • Profiling workflows using Flipper, the React DevTools Profiler, and Systrace
  • Memoization strategies with useMemo, useCallback, and React.memo applied correctly
  • Bundle analysis using Metro bundler tools and source-map-explorer
  • Native module design patterns that minimize bridge crossing frequency
  • Startup time optimization through deferred initialization and splash screen management

How to Use It?

Basic Usage

Apply the skill to any task involving React Native performance. A typical starting point is auditing re-renders using the React DevTools Profiler or adding why-did-you-render to your development build:

import React from 'react';

if (process.env.NODE_ENV === 'development') {
  const whyDidYouRender = require('@welldone-software/why-did-you-render');
  whyDidYouRender(React, { trackAllPureComponents: true });
}

Specific Scenarios

Scenario 1: Eliminating FlatList jank with FlashList

Replace FlatList with FlashList from Shopify to reduce re-render cycles and improve scroll performance on large lists:

import { FlashList } from '@shopify/flash-list';

<FlashList
  data={items}
  renderItem={({ item }) => <ItemComponent item={item} />}
  estimatedItemSize={80}
/>

Scenario 2: Moving animations off the JS thread

Use Reanimated 2 shared values and worklets to ensure animations run on the UI thread regardless of JS thread load:

import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';

const offset = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({ transform: [{ translateX: offset.value }] }));

Real-World Examples

  • A news application reduced TTI by 40 percent by deferring non-critical module initialization and enabling Hermes on Android.
  • An e-commerce app eliminated scroll jank by migrating product listing screens from FlatList to FlashList and memoizing item components.

When to Use It?

Use Cases

  • Optimizing scroll performance in list-heavy screens
  • Reducing app startup time and improving TTI metrics
  • Debugging frame drops reported through crash analytics or user feedback
  • Auditing a codebase before a major release for performance regressions
  • Designing state management architecture to minimize render cascades
  • Integrating native modules with minimal bridge crossing overhead
  • Preparing an application for production profiling and monitoring

Important Notes

Requirements

  • React Native 0.68 or later is recommended for Hermes support on both iOS and Android
  • Reanimated 2 requires additional Babel plugin configuration in babel.config.js
  • FlashList requires a peer dependency on Reanimated and a minimum React Native version of 0.64