React Native Performance skill for Codex

Measured fixes for jank and slow starts. Codex profiles a release build on a slow Android phone, names which thread drops frames, and fixes the single largest cost before touching anything else. It moves long lists to FlashList, lets React Compiler or targeted memoisation stop wasted renders, shifts gestures onto Reanimated worklets, and trims cold start, keeping only changes that move a median measurement.

  • Mobile & Desktop
  • $react-native-performance
  • 155 lines
  • 2 files
  • ~1,900 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- react-native-performance

Installs to ~/.agents/skills/react-native-performance/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $react-native-performance, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Finds and fixes React Native performance problems with measurements - dropped frames on the JS and UI threads, slow FlatList scrolling fixed with FlashList, wasted re-renders found with the React DevTools Profiler and React Compiler, Reanimated worklets for gestures, image decoding, and cold start time with Hermes, lazy loading and Android vitals thresholds. Use when a list janks, a screen feels slow to open, typing lags, animations stutter, app startup is slow, or before a release on low-end Android devices.

Use it when

  • A FlatList or ScrollView drops frames while scrolling
  • Typing, taps or navigation feel delayed
  • Cold start time is high in Android vitals or user reports
  • Gesture or scroll-linked animations stutter

Use something else when

How to check the result

Before and after numbers come from the same release build type on the same device, with the median of at least three scroll runs or five cold starts reported.

The mistake it prevents

Profiling a debug build or an iOS simulator, then shipping memoisation changes that make no difference on real low-end Android hardware.

~/.agents/skills/react-native-performance/SKILL.md

--- name: react-native-performance description: Finds and fixes React Native performance problems with measurements - dropped frames on the JS and UI threads, slow FlatList scrolling fixed with… license: MIT ---

Done well means: every fix is justified by a before/after measurement on a release build on a real low-to-mid-range Android device, the frame budget (16.7 ms at 60 Hz, 8.3 ms at 120 Hz) holds on the screens users scroll most, and cold start stays well under the Android vitals “excessive” bar of 5 s.

Rules before profiling

  • Measure release builds only. Debug builds run unoptimised JS with dev checks and can be 5-10x slower; numbers from them are meaningless.
  • Measure on the slowest device you support (a cheap Android phone), not a flagship or the iOS simulator.
  • Identify which thread drops frames. The React Native perf monitor (Dev Menu, Perf Monitor) shows JS and UI FPS separately:
SymptomThreadUsual cause
JS FPS drops, UI fine, taps respond lateJSRe-renders, heavy work in render, big JSON parse
UI FPS drops while JS is fineUI / renderOverdraw, huge images, shadows, too many views
Both drop during scrollBothList virtualisation off or item components too heavy
Animation stutters only while JS is busyJS-driven animationMove to Reanimated worklets or native driver

Workflow

- [ ] 1. Read AGENTS.md; note RN version, Expo SDK, architecture (New Arch is default since RN 0.76)
- [ ] 2. Reproduce on a release build and record a baseline number
- [ ] 3. Profile (tool table below) and name the single biggest cost
- [ ] 4. Fix that one cost
- [ ] 5. Re-measure under the same conditions; keep the fix only if the number moved
- [ ] 6. Repeat until the target is met; report before/after
QuestionTool
Which components re-render and whyReact DevTools Profiler inside React Native DevTools (press j in Metro), “record why each component rendered”
Where JS time goesReact Native DevTools Performance panel / Hermes sampling profiler
Native rendering, memory, main threadAndroid Studio Profiler, Xcode Instruments (Time Profiler, Animation Hitches)
Repeatable Android score for CIFlashlight (flashlight measure) on a device
Startup timing in productionCustom marks with performance.mark or a startup-trace library, plus Play Console Android vitals

Lists

Replace FlatList for long or heterogeneous lists with FlashList (v2 requires the New Architecture and no longer needs estimatedItemSize):

import { FlashList } from '@shopify/flash-list';

<FlashList
  data={messages}
  keyExtractor={(m) => m.id}
  getItemType={(m) => m.kind}              // recycle text rows with text rows
  renderItem={({ item }) => <MessageRow message={item} />}
/>
  • renderItem components must be cheap and memoised; FlashList recycles views, so local useState in a row leaks between items unless keyed by item id.
  • Stable keyExtractor from data IDs, never the index.
  • Keep images in rows sized explicitly and served at display size.
  • Staying on FlatList? Set getItemLayout for fixed heights, tune windowSize and maxToRenderPerBatch, and avoid removeClippedSubviews on iOS when items have complex layouts (it can blank cells).

Re-renders

  1. Profile first. Look for components rendering on every keystroke or every scroll event.
  2. Enable React Compiler (default in new Expo templates since SDK 54; otherwise add babel-plugin-react-compiler). It auto-memoises components and hooks that follow the Rules of React. Run npx react-compiler-healthcheck or the ESLint plugin to find components it skips.
  3. Without the compiler, fix in this order:
    • Move state down to the component that uses it.
    • Split contexts: a context value that changes on every keystroke re-renders every consumer. Use a store with selectors (Zustand, Jotai, Redux useSelector).
    • React.memo heavy children and pass them stable props (useCallback, useMemo) only where the profiler shows the waste.
  4. Never JSON.stringify or sort large arrays in render; derive once in the store or with useMemo keyed on the source data.

Animations and gestures

  • Run gesture-driven and layout animations on the UI thread with Reanimated (useSharedValue, useAnimatedStyle) and react-native-gesture-handler.
  • Do not call setState from onScroll or gesture updates. Use shared values, and only hop to JS (runOnJS / scheduleOnRN in Reanimated 4) for discrete events.
  • Classic Animated: always useNativeDriver: true for transform and opacity.
  • Avoid animating width, height or top on large trees; animate transform.

Images

  • Use expo-image (or FastImage on bare RN) with disk caching and recyclingKey inside recycled lists.
  • Request thumbnails from the server at roughly display size times pixel ratio. A 4000 px photo decoded into a 64 pt avatar burns memory and UI-thread time.
  • Prefer contentFit over wrapping images in extra views for cropping.

Startup

Targets: cold start under 2 s on a mid-range Android device is good; Android vitals flags cold starts of 5 s or more, warm 2 s or more, hot 1.5 s or more as excessive.

  • Confirm Hermes is on (default). With Hermes the bundle ships as precompiled bytecode.
  • Defer work out of module scope: no SDK init() calls, large JSON imports or storage reads at top level of the entry file unless the first screen needs them.
  • Lazy-load heavy screens and libraries (React.lazy, dynamic import(); Metro inline requires are on by default in recent templates).
  • Render the first screen from cached data, then refresh. Keep the splash screen only until that first meaningful frame.
  • Audit bundle size: npx expo export then inspect with Expo Atlas (EXPO_ATLAS=true npx expo start) or react-native-bundle-visualizer. Watch for moment-style locale bundles, full lodash, and icon fonts imported whole.
  • Android: add a Baseline Profile for the native side when startup is dominated by native init.

Gotchas

  • console.log in hot paths is expensive in release builds too unless stripped; use babel-plugin-transform-remove-console for production or a logger that no-ops.
  • useEffect chains that set state after mount cause double renders on every screen open; derive values instead.
  • Large Text inside ScrollView (thousands of lines) is not virtualised; use a list.
  • Shadows on Android without a background colour, and overflow: hidden with border radius on many items, cost UI-thread time; flatten where possible.
  • Memory growth over navigation usually means listeners or intervals not cleaned up in effect cleanups, or screens kept mounted in a stack. Check with the platform profilers.

Verification

  • Same device, same data set, same release build type for before and after.
  • For lists: scroll the same distance three times and compare dropped frames or Flashlight score; report the median.
  • For startup: cold start (force stop, then launch) at least five times; report median. adb shell am start -W -n com.acme.app/.MainActivity prints TotalTime on Android.
  • If a fix does not move the number beyond noise, revert it; complexity without gain is a regression.

Report template

**Device / build:** Galaxy A15, Android 14, release AAB, Hermes on
| Scenario | Before | After | Change |
|---|---|---|---|
| Inbox scroll, 500 rows (Flashlight score) | 41 | 78 | FlatList -> FlashList, memo rows |
| Cold start TotalTime (median of 5) | 3.9 s | 2.1 s | deferred analytics init, lazy settings screen |
**Not fixed:** chat screen keyboard lag (JS thread busy in markdown parser), next step: move parsing off render

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "React Native Performance"
  short_description: "Measured fixes for jank and slow starts"
  default_prompt: "$react-native-performance Scrolling the inbox list stutters on Android, fix it"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem