Performance Optimization
You are performing a systematic performance diagnosis on a React Native application. Follow this step-by-step process.
When to Use This Skill
Invoke when:
- App feels slow or janky
- Startup time is too long
- Lists scroll poorly
- Animations stutter
- Bundle size is too large
- Memory usage is high
Diagnostic Process
Step 1: Identify the Problem
| Symptom | Likely Cause | First Check |
|---|---|---|
| Slow startup | Large bundle, heavy init | Bundle size, eager imports |
| Janky scrolling | List renderer, heavy cells | FlatList vs FlashList, cell complexity |
| Stuttering animations | JS thread blocking | Worklets, useAnimatedStyle |
| High memory | Leaks, large images | Image caching, subscription cleanup |
| Slow navigation | Heavy screens, eager loading | Lazy loading, screen weight |
Step 2: Measure
Bundle Analysis:
npx react-native-bundle-visualizer
# or for Expo:
npx expo export --dump-sourcemap
Target: < 1.5MB JavaScript bundle
FPS Monitoring:
- Enable Perf Monitor in dev menu
- Or use
useFrameCallbackfrom Reanimated - Target: 60fps constant, no drops below 55fps
TTI (Time to Interactive):
- Measure from app launch to first interactive frame
- Target: < 2 seconds on mid-range device
Memory:
- Use Xcode Instruments (iOS) or Android Profiler
- Watch for monotonically increasing memory (leak indicator)
Step 3: Optimize by Category
Bundle Size:
- Analyze imports — find large dependencies
- Lazy load screens:
React.lazy()withSuspense - Tree-shake unused exports
- Replace large libraries with lighter alternatives
- Use Hermes (enabled by default in Expo SDK 50+)
Rendering:
- Replace
FlatListwithFlashList+estimatedItemSize - Memoize expensive components:
React.memo - Use
useCallbackfor event handlers passed to children - Avoid inline object/array creation in JSX
- Profile re-renders with React DevTools Profiler
Animations:
- Move all animations to UI thread (Reanimated worklets)
- Use
useAnimatedStyleinstead of inline animated values - Batch related animations with
withSequence/withDelay - Reduce animated property count (transform is cheaper than layout)
- Use
cancelAnimationfor cleanup
Images:
- Use
expo-imagewithcachePolicy="memory-disk" - Serve correct sizes (don't scale 4K images to thumbnails)
- Use WebP format for smaller file sizes
- Implement progressive loading (blurhash placeholder)
Memory:
- Clean up listeners in
useEffectreturn - Remove event subscriptions on unmount
- Use WeakRef for caches where appropriate
- Paginate large data sets (don't load all at once)
Step 4: Verify Improvements
Re-measure after each optimization:
## Performance Report
| Metric | Before | After | Target | Status |
|--------|--------|-------|--------|--------|
| Bundle size | 2.1MB | 1.4MB | <1.5MB | PASS |
| TTI | 3.2s | 1.8s | <2s | PASS |
| List FPS | 45fps | 60fps | 60fps | PASS |
| Memory (5min) | +80MB | +12MB | <20MB | PASS |
Source: JubaKitiashvili/everything-react-native-expo — distributed by TomeVault.