Skip to main content
Profiling & Monitoring

Module Overview

Estimated Time: 4 hours | Difficulty: Advanced | Prerequisites: Performance Optimization, Debugging modules
Performance problems in mobile apps do not announce themselves — they accumulate silently until users feel the app is “slow” and leave a 2-star review. The challenge is that React Native apps have two execution environments (JavaScript thread and native UI thread), and a bottleneck in either one produces the same visible symptom: dropped frames and sluggish interactions. Profiling is detective work. You need different tools for different suspects: the React Profiler for re-render issues, native profilers (Xcode Instruments, Android Profiler) for native-side bottlenecks, and custom metrics for production monitoring. This module covers them all. What You’ll Learn:
  • React DevTools Profiler
  • Flipper performance plugins
  • Native profiling tools
  • Production monitoring setup
  • Custom performance metrics
  • APM integration

Performance Profiling Overview


React DevTools Profiler

The React Profiler answers the question: “Which components are rendering, how often, and how long does each render take?” This is your first stop when investigating JavaScript-thread performance issues. The key insight: a component re-rendering is not inherently bad — re-rendering unnecessarily (with the same props and state) is where performance is wasted.

Setting Up Profiler

Custom Profiler Component


Flipper Integration

Setup Flipper

Custom Flipper Plugin

Performance Tracking Hook


Native Profiling Tools

iOS Instruments

Android Profiler

Systrace Integration


Custom Performance Metrics

Built-in tools show you what is happening during development, but production is where performance actually matters. Users on 3-year-old Android phones with spotty cellular connections experience your app very differently from your development MacBook. Custom metrics bridge this gap by collecting timing data from real user sessions. The pattern below is a lightweight performance tracking service that measures arbitrary operations, buffers the results, and flushes them to your backend after interactions complete (so the metrics collection itself does not cause jank).

Performance Metrics Service

App Startup Tracking

Usage in App Entry


APM Integration

Application Performance Monitoring (APM) tools give you a production-grade observability layer: automatic transaction tracing, error correlation, and dashboards that show how your app performs across device types, OS versions, and network conditions. If custom metrics are a thermometer, APM is a full medical workup. For most React Native teams, Sentry is the best starting point — it handles both crash reporting and performance monitoring in a single SDK, with excellent React Native support.

Sentry Performance Monitoring

Custom Spans

New Relic Integration


Frame Rate Monitoring

Frame rate is the single most important performance metric for mobile apps. Anything below 60 FPS (16.67ms per frame) is perceptible as jank. Drops below 30 FPS feel genuinely broken. The hook below measures frame timing using requestAnimationFrame and classifies frames as “jank” when they exceed the 16.67ms budget. This is a development tool — do not ship it in production builds, as the measurement itself consumes resources. Use APM tools for production frame rate tracking.

FPS Monitor Component


Memory Monitoring


Performance Dashboard


Choosing the Right Profiling Tool

Each tool in the profiling stack answers a different question. Using the wrong tool wastes time — you will see misleading data or miss the actual bottleneck entirely. Decision framework — where to start:
  1. “The app feels slow” — Start with React DevTools Profiler. Most perceived slowness in React Native comes from unnecessary re-renders on the JS thread.
  2. “Animations are janky” — If the JS thread profiler shows renders completing under 16ms but animations still drop frames, the bottleneck is on the native thread. Switch to Xcode Instruments or Android Profiler.
  3. “Users report slowness but I can’t reproduce it” — You need production metrics. Set up APM (Sentry) and custom performance tracking to see what real users experience on real devices.
  4. “Startup takes too long” — Instrument startup milestones (JS load, first render, interactive). Compare against budgets: cold start under 2 seconds is the target; under 1 second is excellent.
  5. “The app crashes after extended use” — This is likely a memory leak. Use the Android Profiler’s memory timeline or Xcode’s Memory Graph to find retained objects.

Hermes vs. JSC Profiling Differences

If your app runs on Hermes (the default since React Native 0.70 and Expo SDK 48+), profiling behavior differs from JavaScriptCore (JSC) in important ways: To take a Hermes CPU profile programmatically:

Performance Budgets

Defining concrete performance budgets turns vague “make it faster” requests into measurable, actionable targets. These budgets should be enforced in CI — not just aspirational numbers in a wiki. Enforcing budgets in CI:

Common Profiling Pitfalls

Profiling in Development Mode

Development mode enables React’s reconciler warnings, Strict Mode double-renders, and disables most optimizations. Profiling in dev mode will show every component rendering 2x and taking 3-5x longer than production. Always profile release builds. For Expo:

The Observer Effect

The act of profiling changes what you measure. Flipper adds network overhead. The React Profiler’s onRender callback adds JS thread work. The FPS monitor hook from this module uses requestAnimationFrame (which itself consumes frame budget). Profile with one tool at a time, and remove profiling instrumentation from production builds.

Misattributing Bridge Overhead

In the classic React Native architecture (pre-New Architecture), every communication between JS and native crosses the bridge asynchronously. This means a “slow render” might actually be a slow bridge crossing — the JS code finished fast, but the native update was queued behind other bridge traffic. With the New Architecture (Fabric + TurboModules), this is less of an issue because communication is synchronous. Check whether your app uses the New Architecture before spending time optimizing bridge traffic.

Memory Leak False Positives

JS heap growing during a session does not always mean a leak. V8 and Hermes both use generational garbage collectors that may delay collection. A true leak is memory that grows monotonically and never decreases even after GC runs. To confirm: force GC (in Chrome DevTools attached to Hermes, click the trash can icon), then check if memory drops. If it does, you had garbage, not a leak.

Best Practices

Profile in Production Mode

Development mode adds overhead - always profile release builds

Sample in Production

Don’t collect 100% of metrics in production - use sampling

Set Performance Budgets

Define acceptable thresholds and alert when exceeded

Monitor Real Users

Synthetic tests don’t capture real-world conditions

Production Sampling Strategy

Collecting metrics from every user session overwhelms your backend and inflates costs. Use tiered sampling: Adjust these rates upward for smaller user bases. If you have under 10,000 DAU, sample at 100% for everything — the volume is manageable and you cannot afford to miss patterns.

Next Steps

Module 36: Error Handling & Crash Reporting

Learn to handle errors gracefully and report crashes effectively