Claude Code for Mobile Performance: React Native Optimization and Bundle Size — Claude Skills 360 Blog
Blog / Mobile / Claude Code for Mobile Performance: React Native Optimization and Bundle Size
Mobile

Claude Code for Mobile Performance: React Native Optimization and Bundle Size

Published: July 27, 2026
Read time: 8 min read
By: Claude Skills 360

React Native performance problems have predictable patterns: FlatList rendering every scroll frame, re-renders cascading down component trees, JavaScript bundle parsing blocking startup, and images loaded at wrong sizes. Claude Code identifies these patterns and generates the optimized versions — memoized components, virtualized lists with proper keyExtractor, and Hermes-optimized bundles.

FlatList Optimization

Our FlatList with 1000 products is janky on scroll.
Items re-render on every scroll event. Fix it.
// Before — every scroll triggers re-render of visible items
function ProductList({ products, onAddToCart }: Props) {
  return (
    <FlatList
      data={products}
      renderItem={({ item }) => (
        <ProductCard
          product={item}
          onAddToCart={onAddToCart} // New function reference every render
        />
      )}
    />
  );
}

// After — stable references prevent re-renders
const ProductCard = React.memo(({ product, onAddToCart }: ProductCardProps) => {
  // Only re-renders when product or onAddToCart reference changes
  return (
    <TouchableOpacity onPress={() => onAddToCart(product.id)}>
      <FastImage
        source={{ uri: product.imageUrl, priority: FastImage.priority.normal }}
        style={styles.image}
        resizeMode={FastImage.resizeMode.cover}
      />
      <Text>{product.name}</Text>
      <Text>${(product.priceCents / 100).toFixed(2)}</Text>
    </TouchableOpacity>
  );
}, (prev, next) => {
  // Custom comparison — only re-render if these change
  return prev.product.id === next.product.id &&
         prev.product.priceCents === next.product.priceCents;
});

function ProductList({ products, onAddToCart }: Props) {
  // Stable callback reference
  const handleAddToCart = useCallback((productId: string) => {
    onAddToCart(productId);
  }, [onAddToCart]);

  // Stable keyExtractor
  const keyExtractor = useCallback((item: Product) => item.id, []);

  // Optimize item layout for fixed-size items (avoids layout recalculation)
  const getItemLayout = useCallback((_: any, index: number) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  }), []);

  return (
    <FlatList
      data={products}
      renderItem={({ item }) => (
        <ProductCard product={item} onAddToCart={handleAddToCart} />
      )}
      keyExtractor={keyExtractor}
      getItemLayout={getItemLayout}
      maxToRenderPerBatch={10}       // Render 10 items per batch (default 10)
      updateCellsBatchingPeriod={50} // Milliseconds between batch renders
      windowSize={5}                 // Render 5 viewport heights (default 21)
      removeClippedSubviews={true}   // Unmount items far off screen
      initialNumToRender={8}         // Only render 8 for initial mount
      onEndReachedThreshold={0.5}    // Load more when 50% from end
    />
  );
}

Reducing Re-Renders

Help me find and fix unnecessary re-renders in our profile screen.
The whole screen re-renders when any part of the user object changes.
// Problem: entire ProfileScreen renders when ANY user property changes
function ProfileScreen() {
  const user = useUserStore(state => state.user); // Subscribes to entire user object

  return (
    <>
      <Avatar url={user.avatarUrl} />
      <UserStats orders={user.orderCount} joined={user.joinedAt} />
      <BadgeList badges={user.badges} />
    </>
  );
}

// Fix: granular selectors — each component gets only what it needs
const Avatar = React.memo(({ url }: { url: string }) => (
  <FastImage source={{ uri: url }} style={styles.avatar} />
));

function AvatarContainer() {
  // Only re-renders when avatarUrl changes
  const avatarUrl = useUserStore(state => state.user.avatarUrl);
  return <Avatar url={avatarUrl} />;
}

const UserStats = React.memo(({ orders, joined }: { orders: number; joined: string }) => (
  <View>
    <Text>{orders} orders</Text>
    <Text>Member since {joined}</Text>
  </View>
));

function UserStatsContainer() {
  // Zustand shallow comparison — only re-renders when these two change
  const { orderCount, joinedAt } = useUserStore(
    state => ({ orderCount: state.user.orderCount, joinedAt: state.user.joinedAt }),
    shallow, // Prevents re-render if other user fields change
  );
  return <UserStats orders={orderCount} joined={joinedAt} />;
}

Hermes Profiling

Our app takes 3.2 seconds to become interactive on mid-range Android.
Find the startup bottleneck.
# Enable Hermes (already default in RN 0.70+, but verify)
# android/app/build.gradle:
# project.ext.react = [
#   enableHermes: true,
# ]

# Profile startup:
# 1. Enable remote debugging: Shake device → Debug → Enable Remote Debugging off
#    Instead: use Hermes inspector

# Start Metro with profiling
npx react-native start

# Capture startup profile via adb
adb shell am start -n com.yourapp/.MainActivity

# Download Hermes timeline
adb pull /sdcard/Android/data/com.yourapp/files/sampling_profiler_trace.cpuprofile .

Claude Code can analyze the profiler output:

Parse this Hermes CPU profile and find:
1. What's blocking the JS thread on startup?
2. Which modules are slowest to initialize?
3. What can be deferred until after first interaction?
(paste profile JSON)

Common findings and fixes:

// Problem: importing heavy libraries at module init time
import moment from 'moment'; // 300KB, initializes on startup
import _ from 'lodash';       // 70KB, loads all functions

// Fix: lazy imports for non-critical paths
const getDateFormatter = () => import('./utils/dateFormat');
// Or use lighter alternatives:
import { format } from 'date-fns'; // Tree-shakeable, ~20KB
import { debounce } from 'lodash-es/debounce'; // Single function

Image Performance

Our app has slow image loading and high memory usage from large images.
We're showing product photos at thumbnail size but loading full resolution.
// Use react-native-fast-image for caching + priority control
// AND specify correct size to avoid loading 4K images at 100x100px

// Product thumbnail component
function ProductThumbnail({ product }: { product: Product }) {
  return (
    <FastImage
      source={{
        uri: getOptimizedImageUrl(product.imageUrl, {
          width: 200,  // Match display size × pixel density
          height: 200,
          format: 'webp',
          quality: 80,
        }),
        priority: FastImage.priority.normal,
        cache: FastImage.cacheControl.immutable, // Never re-fetch same URL
      }}
      style={styles.thumbnail}
      resizeMode={FastImage.resizeMode.cover}
    />
  );
}

// Image URL optimizer (calls your image CDN)
function getOptimizedImageUrl(url: string, opts: { width: number; height: number; format: string; quality: number }) {
  // Cloudflare Images / Imgix / Cloudinary transformation URL
  const pixelDensity = PixelRatio.get(); // 2 or 3 on modern phones
  return `${url}?w=${opts.width * pixelDensity}&h=${opts.height * pixelDensity}&f=${opts.format}&q=${opts.quality}`;
}

Bundle Size Analysis

# Analyze what's in the bundle
npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output /tmp/main.bundle \
  --assets-dest /tmp/assets \
  --sourcemap-output /tmp/main.bundle.map

# Visualize with source-map-explorer
npx source-map-explorer /tmp/main.bundle /tmp/main.bundle.map
The source map explorer shows moment.js is 230KB.
We only use moment.format() in one place. Replace it.
// Before
import moment from 'moment';
const formatted = moment(date).format('MMM D, YYYY');

// After — native Intl API, zero bundle cost
const formatted = new Intl.DateTimeFormat('en-US', {
  month: 'short',
  day: 'numeric',
  year: 'numeric',
}).format(new Date(date));

For the React Native project structure and navigation setup, see the React Native navigation guide. For mobile CI/CD that runs performance regression tests, see the mobile CI/CD guide. The Claude Skills 360 bundle includes mobile performance skill sets for React Native optimization, profiling, and bundle reduction. Start with the free tier to try performance analysis prompts.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free