Documentation Index
Fetch the complete documentation index at: https://mintlify.com/facebook/react-native/llms.txt
Use this file to discover all available pages before exploring further.
Profiling React Native Apps
Profiling is essential for identifying performance bottlenecks in your React Native application. Learn how to use various profiling tools to analyze and optimize your app.
Enable the built-in performance monitor to see real-time metrics:
// In-app developer menu
// Shake device or press Cmd+D (iOS) / Cmd+M (Android)
// Select "Show Performance Monitor"
The monitor displays:
- JavaScript frame rate
- UI frame rate
- Views count
- RAM usage
Installation
npm install -g react-devtools
Using the Profiler
Connect your app
Start your React Native app with DevTools enabled. The app will automatically connect.
Record a profiling session
Click the Profiler tab, then click the record button to start profiling.
Analyze results
View flame charts showing component render times and why components re-rendered.
Using the Profiler API
import React, { Profiler } from 'react';
function onRenderCallback(
id, // the "id" prop of the Profiler tree that has just committed
phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
actualDuration, // time spent rendering the committed update
baseDuration, // estimated time to render the entire subtree without memoization
startTime, // when React began rendering this update
commitTime, // when React committed this update
interactions // the Set of interactions belonging to this update
) {
console.log(`${id} (${phase}) took ${actualDuration}ms`);
if (actualDuration > 16) {
console.warn(`${id} exceeded 16ms frame budget!`);
}
}
const App = () => (
<Profiler id="App" onRender={onRenderCallback}>
<NavigationContainer>
<MainNavigator />
</NavigationContainer>
</Profiler>
);
React Native implements the Web Performance API:
import { performance } from 'react-native';
// Mark points in time
performance.mark('fetch-start');
await fetchData();
performance.mark('fetch-end');
// Measure duration between marks
performance.measure('data-fetch', 'fetch-start', 'fetch-end');
// Get measurements
const measures = performance.getEntriesByType('measure');
measures.forEach(measure => {
console.log(`${measure.name}: ${measure.duration}ms`);
});
Automatically monitor performance entries:
import { PerformanceObserver } from 'react-native';
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration}ms`);
// Log slow operations
if (entry.duration > 100) {
console.warn('Slow operation detected:', entry);
}
});
});
// Observe all measure entries
observer.observe({ entryTypes: ['measure'] });
// Cleanup
observer.disconnect();
JavaScript Thread Profiling
Enable remote debugging
Open the in-app developer menu and select “Debug”.
Open Chrome DevTools
Navigate to chrome://inspect in Chrome.
Start profiling
Go to the Performance tab and click Record.
Interact with app
Perform the actions you want to profile.
Analyze results
Stop recording and analyze the flame chart for bottlenecks.
Using Hermes Profiler
Hermes includes a built-in sampling profiler:
// Enable Hermes profiling
const Hermes = require('hermes-engine');
// Start profiling
Hermes.samplingProfiler.enable();
// Your code here
expensiveOperation();
// Stop profiling and get profile
const profile = Hermes.samplingProfiler.stop();
// Convert to Chrome format
const chromeProfile = Hermes.samplingProfiler.dumpSampledTraceToFile();
console.log('Profile saved:', chromeProfile);
Visualize the profile:
npx react-native profile-hermes [profile-file]
Native Thread Profiling
Android Profiling
Systrace
Capture system-level performance:
# Record 5-second trace
python $ANDROID_HOME/platform-tools/systrace/systrace.py \
--time=5 \
-o trace.html \
sched gfx view wm am dalvik input
Open trace.html in Chrome to analyze.
Android Studio Profiler
Open Android Studio
Open your project in Android Studio.
Run profiler
Click View > Tool Windows > Profiler.
Select process
Click the + button and select your app process.
Profile CPU
Click the CPU row to start CPU profiling:
- Sample C/C++ Functions
- Trace System Calls
- Sample Java Methods
Method Tracing
// Add to Android code
import android.os.Debug
Debug.startMethodTracing("MyApp")
// Code to profile
Debug.stopMethodTracing()
// Pull trace file
// adb pull /sdcard/Android/data/com.yourapp/files/MyApp.trace
iOS Profiling
Instruments
Build for profiling
In Xcode, select Product > Profile (Cmd+I).
Choose template
Select a profiling template:
- Time Profiler: CPU usage
- Allocations: Memory allocations
- Leaks: Memory leaks
- System Trace: System-level events
Record and analyze
Click the record button, use your app, then stop and analyze results.
Time Profiler
Analyze CPU usage:
// Add to iOS code for manual instrumentation
#import <os/signpost.h>
os_log_t log = os_log_create("com.yourapp", "Performance");
os_signpost_id_t spid = os_signpost_id_generate(log);
os_signpost_interval_begin(log, spid, "ExpensiveOperation");
// Code to profile
os_signpost_interval_end(log, spid, "ExpensiveOperation");
Component Render Profiling
Why Did You Render
Detect unnecessary re-renders:
npm install @welldone-software/why-did-you-render
// wdyr.js
import React from 'react';
if (__DEV__) {
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
trackHooks: true,
logOwnerReasons: true,
});
}
// index.js
import './wdyr'; // Must be first
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('MyApp', () => App);
React.memo Profiling
import React, { memo } from 'react';
const MyComponent = memo(
({ data }) => {
console.log('MyComponent rendered');
return <View>{data}</View>;
},
(prevProps, nextProps) => {
// Return true if props are equal (skip render)
const areEqual = prevProps.data === nextProps.data;
if (!areEqual) {
console.log('Re-rendering due to data change');
}
return areEqual;
}
);
Frame Rate Analysis
Monitor Frame Drops
import { InteractionManager } from 'react-native';
class FrameMonitor {
constructor() {
this.frameCount = 0;
this.lastTime = Date.now();
this.requestAnimationFrame = this.requestAnimationFrame.bind(this);
}
start() {
this.handle = requestAnimationFrame(this.requestAnimationFrame);
}
requestAnimationFrame() {
this.frameCount++;
const now = Date.now();
const elapsed = now - this.lastTime;
if (elapsed >= 1000) {
const fps = (this.frameCount / elapsed) * 1000;
console.log(`FPS: ${fps.toFixed(2)}`);
if (fps < 55) {
console.warn('Frame rate dropped below 55 FPS!');
}
this.frameCount = 0;
this.lastTime = now;
}
this.handle = requestAnimationFrame(this.requestAnimationFrame);
}
stop() {
cancelAnimationFrame(this.handle);
}
}
const monitor = new FrameMonitor();
monitor.start();
Memory Profiling
Track Memory Usage
import { NativeModules } from 'react-native';
const getMemoryInfo = () => {
if (__DEV__) {
const { performance } = require('react-native');
if (performance.memory) {
return {
usedJSHeapSize: performance.memory.usedJSHeapSize,
totalJSHeapSize: performance.memory.totalJSHeapSize,
jsHeapSizeLimit: performance.memory.jsHeapSizeLimit,
};
}
}
return null;
};
// Log memory periodically
setInterval(() => {
const memory = getMemoryInfo();
if (memory) {
const usedMB = (memory.usedJSHeapSize / 1024 / 1024).toFixed(2);
const totalMB = (memory.totalJSHeapSize / 1024 / 1024).toFixed(2);
console.log(`Memory: ${usedMB}MB / ${totalMB}MB`);
}
}, 5000);
Find Memory Leaks
import React, { useEffect, useRef } from 'react';
const MyComponent = () => {
const intervalRef = useRef(null);
useEffect(() => {
// Good: cleanup on unmount
intervalRef.current = setInterval(() => {
console.log('Interval running');
}, 1000);
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []);
return <View />;
};
Network Profiling
Monitor Network Requests
import { XMLHttpRequest } from 'react-native';
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._startTime = Date.now();
this._url = url;
this._method = method;
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
this.addEventListener('load', function() {
const duration = Date.now() - this._startTime;
console.log(`${this._method} ${this._url}: ${duration}ms`);
});
return originalSend.apply(this, arguments);
};
Profiling Best Practices
- Profile on Real Devices: Simulators/emulators don’t represent real performance
- Use Production Builds: Development mode has overhead
- Test on Low-End Devices: Identify performance issues early
- Profile Specific Scenarios: Focus on critical user journeys
- Baseline First: Establish baseline metrics before optimization
- Profile Incrementally: Profile after each optimization
Profiling Checklist
Next Steps