All skills
callstackincubator avatar

/react-native-best-practices

@fa0bad0 official

Provides React Native performance optimization guidelines for FPS, TTI, bundle size, memory leaks, re-renders, and animations. Applies to tasks involving Hermes optimization, JS thread blocking, bridge overhead, FlashList, native modules, or debugging jank and frame drops.

Use this Skill: https://skilld.dev/gh/callstackincubator/agent-skills/react-native-best-practices

This session only. Nothing lands on disk.

referencesbundle-analyze-js.md

≈1.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Skill: Analyze JS Bundle Size

Use source-map-explorer and Expo Atlas to visualize what's in your JavaScript bundle.

Quick Command

# React Native CLI
npx react-native bundle \
  --entry-file index.js \
  --bundle-output output.js \
  --platform ios \
  --sourcemap-output output.js.map \
  --dev false --minify true && \
npx source-map-explorer output.js --no-border-checks

# Expo
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios && npx expo-atlas

When to Use

  • JS bundle seems too large
  • Want to identify heavy dependencies
  • Investigating startup time issues
  • Before/after optimization comparison

Note: This skill involves visual treemap output (source-map-explorer, Expo Atlas). When regression checks include device flows, use agent-device for app evidence; install it through the environment's approved/trusted path or ask the user if verification needs it and it is missing. Treemap analysis itself may still require exported reports, browser screenshots, or human review. Record the largest modules and before/after bundle sizes in text when asking an agent to reason about them.

Understanding Hermes Bytecode

Release builds using Hermes, the default engine in modern React Native, ship Hermes bytecode rather than raw JavaScript:

  • Skips parsing at runtime
  • Still benefits from smaller bundles
  • Heavy imports still execute on startup

Impact of bundle size:

  • Larger bytecode = longer download from store
  • More imports on init path = slower TTI

Development builds fetch JS from the dev server, and non-Hermes engines have different startup tradeoffs. Smaller bytecode helps app size and startup, but startup also depends on what executes on the initialization path. Imports that eagerly touch native modules can defeat Turbo Module lazy loading and hurt TTI.

Method 1: source-map-explorer

Generate Bundle with Source Map

React Native CLI:

npx react-native bundle \
  --entry-file index.js \
  --bundle-output output.js \
  --platform ios \
  --sourcemap-output output.js.map \
  --dev false \
  --minify true

Expo (SDK 51+):

npx expo export --platform ios --source-maps --output-dir dist
# Bundle at: dist/ios/_expo/static/js/ios/*.js
# Source map at: dist/ios/_expo/static/js/ios/*.map

Analyze

npx source-map-explorer output.js --no-border-checks

Note: --no-border-checks needed due to Metro's non-standard source maps.

Opens browser with treemap visualization:

Bundle Treemap from source-map-explorer

The treemap shows:

  • Hierarchy: node_modules/ → react-native/ → Libraries/ → individual files
  • Size: Box area proportional to file size (KB shown in labels)
  • Major components visible:
    • react-native (724.18 KB, 80.5%)
    • Renderer (208.44 KB) - ReactNativeRenderer-prod.js, ReactFabric-prod.js
    • Components (125.29 KB) - Touchable, ScrollView, etc.
    • Animated (79.48 KB) - Animation system
    • virtualized-lists (57.57 KB) - FlatList internals

Click on any section to drill down into that directory.

Limitation: May lose ~30% info due to mapping issues.

Method 2: Expo Atlas

More accurate for Expo projects (or with workaround for bare RN).

For Expo Projects

# Start with Atlas enabled
EXPO_UNSTABLE_ATLAS=true npx expo start --no-dev

# Or export
EXPO_UNSTABLE_ATLAS=true npx expo export

Then launch UI:

npx expo-atlas

Expo Atlas Treemap

Expo Atlas provides more accurate visualization for Expo projects, with similar treemap interface showing module sizes and dependencies.

For Non-Expo Projects

Use expo-atlas-without-expo package.

Method 3: Re.Pack Bundle Analysis (Webpack/Rspack)

If using Re.Pack:

webpack-bundle-analyzer

rspack build --analyze

bundle-stats / statoscope

# Generate stats
npx react-native bundle \
  --platform android \
  --entry-file index.js \
  --dev false \
  --minify true \
  --json stats.json

# Analyze
npx bundle-stats --html --json stats.json

Rsdoctor

// rspack.config.js
const { RsdoctorRspackPlugin } = require('@rsdoctor/rspack-plugin');

module.exports = {
  plugins: [
    process.env.RSDOCTOR && new RsdoctorRspackPlugin(),
  ].filter(Boolean),
};

Run with:

RSDOCTOR=true npx react-native start

What to Look For

Red Flags

Finding Problem Solution
Entire library imported Barrel exports Use direct imports
Duplicate packages Multiple versions Dedupe in package.json
Dev dependencies in bundle Incorrect imports Check conditional imports
Large polyfills Unnecessary for Hermes Remove (see native-sdks-over-polyfills.md)
Moment.js with locales Bloated date library Switch to date-fns or dayjs

Common Offenders

  • Lodash full import: Prefer built-ins or specific imports
  • Moment.js: Replace with date-fns or dayjs
  • Intl polyfills: Check Hermes API and method coverage before removing them
  • AWS SDK: Import specific services only

Code Examples

Identify Barrel Import Impact

// BAD: Imports entire library through barrel
import { format } from 'date-fns';

// In bundle: All of date-fns loaded

// GOOD: Direct import
import format from 'date-fns/format';

// In bundle: Only format function

Comparing Bundles

source-map-explorer

# Generate baseline
npx react-native bundle ... --bundle-output baseline.js --sourcemap-output baseline.js.map

# Make changes, generate new bundle
npx react-native bundle ... --bundle-output current.js --sourcemap-output current.js.map

# Compare manually in browser

Re.Pack (automated)

npx bundle-stats compare baseline-stats.json current-stats.json

Quick Commands

React Native CLI:

# iOS bundle analysis
npx react-native bundle \
  --entry-file index.js \
  --bundle-output ios-bundle.js \
  --platform ios \
  --sourcemap-output ios-bundle.js.map \
  --dev false \
  --minify true && \
npx source-map-explorer ios-bundle.js --no-border-checks

# Android bundle analysis  
npx react-native bundle \
  --entry-file index.js \
  --bundle-output android-bundle.js \
  --platform android \
  --sourcemap-output android-bundle.js.map \
  --dev false \
  --minify true && \
npx source-map-explorer android-bundle.js --no-border-checks

Expo:

# Use Expo Atlas (recommended for Expo projects)
EXPO_UNSTABLE_ATLAS=true npx expo export --platform ios
npx expo-atlas

Related Skills

Source: SKILL.md on GitHub

1 alert17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill provides a comprehensive performance optimization guide for React Native applications. It contains standard developer workflows using command-line tools and patterns for advanced techniques like remote code splitting, which include detailed security hardening advice.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    11/31 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at fa0bad0. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
  • Performance
  • react-native
  • optimization
  • profiling
  • fps
  • bundle-size
  • memory
  • hermes
  • flatlist
  • animations

README badge

README badge for callstackincubator/agent-skills/react-native-best-practices

Provides React Native performance optimization guidelines covering FPS, TTI, bundle size, memory leaks, re-renders, and animations. Includes reference documentation for Hermes, FlashList, Turbo Modules, React Compiler, Reanimated, and profiling with Xcode and Android Studio.

Generated from the current SKILL.md.

Does this skill cover Expo apps?
Yes. The guidelines apply to both bare React Native and Expo projects. Expo SDK 52+ supports tree shaking and Re.Pack code splitting.
What performance metrics should I measure first?
Start with FPS (use React Native DevTools), Time To Interactive (TTI), bundle size, and memory growth. The skill emphasizes measuring baseline metrics before and after optimization to validate improvements.
Does this cover Hermes optimization?
Yes. The skill includes Hermes-specific guidance such as disabling JS bundle compression on Android to enable mmap, and checking Hermes API coverage before removing polyfills.
What if I'm using a list library other than FlatList or FlashList?
The skill prioritizes FlatList and FlashList as virtualized replacements for ScrollView. It includes library version checks (e.g., FlashList v2 API changes) but focuses on these two libraries.
Does this include native profiling guidance?
Yes. The skill covers profiling with Xcode Instruments (iOS) and Android Studio CPU Profiler, plus guidance on native threading, Turbo Modules, and C++ performance-critical code.

Generated from the current SKILL.md. These answers refresh after source changes.