This episode covers performance: profiling with React DevTools, the Performance Monitor, and native tools, then optimization with Hermes, fewer re-renders, list virtualization, images, and Reanimated animations.

A slow app is the number one reason users abandon an app. But optimizing without measuring is just guessing. The first principle of performance engineering: measure first, then fix.
Episode 18 covers profiling tools — React DevTools, the Performance Monitor, and native profiling — then the optimization techniques that matter: Hermes as the default engine, fewer re-renders, list virtualization, image optimization, and Reanimated animations. Episode 8 already covered lists; this episode widens the focus to the whole app.
React DevTools provides a Profiler tab for recording renders and finding components that re-render unnecessarily. Run it alongside the app:
npm run startThen choose the profiler option from the developer menu, or use the --devtools flag if it's available in the CLI version you use. The Profiler shows each render's duration and flags slow components.
React Native has a built-in Performance Monitor that shows FPS and memory usage on the app screen. Enable it via the developer menu: press m in Metro (Android) or use the shortcut in the development menu, then select Show Perf Monitor.
If the problem is on the native side — native modules, lists, or animations — use the platform tools: Android Studio Profiler for CPU, memory, and network on Android, and Instruments for iOS. Both show UI Thread activity that isn't visible from JavaScript.
Hermes is the JavaScript engine optimized for React Native: faster startup and lower memory. Since 0.76 Hermes is on by default — make sure you're using it, not disabling it.
Bundle size correlates with startup time. Measure the change every release:
npx react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output /tmp/app.bundle
ls -lh /tmp/app.bundlenpx react-native bundle --platform android --dev false produces the production bundle, and ls -lh shows its size. Monitor this size trend to catch regressions early.
Unnecessary re-renders are a common cause of frame drops. The combination already discussed in episodes 5 and 8 works best:
memo wraps components whose props don't change.useCallback stabilizes function references passed as props.useMemo caches heavy calculation values.import React, { memo, useMemo } from "react";
import { Text } from "react-native";
const BarisItem = memo(({ item }) => <Text>{item.nama}</Text>);
function Daftar({ data }) {
const itemDibutuhkan = useMemo(
() => data.filter((item) => item.aktif),
[data]
);
return itemDibutuhkan.map((item) => (
<BarisItem key={item.id} item={item} />
));
}useMemo(() => data.filter(...), [data]) computes the filter only when data changes. Combined with memo, every small state change doesn't shake the whole list.
Downloading a 4000-pixel image to display in a 200-pixel box wastes bandwidth and memory. Prepare image versions matching the display size on the CDN, or resize at upload time as in episode 12.
Use an image library that handles disk caching and efficient formats. expo-image is suitable for Expo projects:
npx expo install expo-imageimport { Image } from "expo-image";
<Image
source="https://cdn.example.com/photo.jpg"
cachePolicy="memory-disk"
recyclingKey={photoId}
style={{ width: 200, height: 200 }}
/>cachePolicy="memory-disk" stores downloaded images on disk so repeatedly scrolled lists don't download again. recyclingKey allows old images to be recycled during list virtualization — reducing reloads.
Episode 8 already covered FlatList virtualization for thousands of items. For animations, move execution to the UI Thread with Reanimated instead of the built-in Animated, which runs on the JS Thread — full details in episode 19. The rule is simple: animations and scroll must not be blocked by JavaScript work.
Tip
Don't optimize before measuring. Record a profile before and after every change, and keep the metrics. Improvements that don't change the numbers don't need to be prioritized.
Episode 18 made the app feel fast: profiling with React DevTools and the Performance Monitor, Hermes as the default engine, fewer re-renders with memo, image optimization, and the combination of virtualization and Reanimated animations.
Key takeaways:
memo, useCallback, and useMemo trim re-renders.In the next episode, episode 19, we'll discuss animations and Reanimated: a comparison of the built-in Animated with worklet-based Reanimated 3+, React Native Gesture Handler, transition patterns, gesture-driven animation, and Lottie for complex animations.