◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SDK 58 — Beta since 09/15 with no stable date yet; running projects in Expo Go has required login since 09/03●WIDGET — A write-up traces an iOS widget that renders black only in production builds●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-04-03Advanced

The Complete Rork App Performance Optimization Guide: Rendering, Memory, and Bundle Size

A comprehensive guide to squeezing every bit of performance out of your Rork app. Covers rendering optimization, memory leak prevention, bundle size reduction, and profiling with Flipper.

performance optimizationReact Native239Rork Max236memory managementrenderingbundle sizeFlipperReanimated10

✦ Premium Article

Setup and context: Performance Is the Product

When building with Rork, it's easy to focus on shipping features and leave performance tuning for "later." But in a world where users make snap judgments—often in the first few seconds—speed and smoothness are every bit as important as functionality. Google's research shows that bounce rates increase by 32% when page load time grows from one second to three. The same principle applies to native apps.

App Store reviews are brutal when it comes to performance. "Laggy," "slow," and "crashes" are among the most common complaints that sink otherwise excellent apps. Conversely, apps that feel responsive and snappy get disproportionate praise, even when their feature sets are comparable to slower competitors.

This guide is written for developers who are ready to go beyond the basics. We'll cover how to systematically optimize React Native and Expo apps generated by Rork and Rork Max—not just with quick tips, but with the underlying reasoning you need to apply these patterns to your own projects. We'll profile with real tools, measure with real numbers, and make decisions based on data rather than guesswork.


Chapter 1: Understanding the React Native Rendering Cycle

How React Native Renders

React Native runs JavaScript on a dedicated JS thread, separate from the native UI thread. When state or props change, React computes a virtual DOM diff and communicates changes to the UI thread. In the classic architecture, this communication crosses a bridge—a serialization/deserialization bottleneck that can cause frame drops. The New Architecture (JSI + Fabric) reduces this overhead significantly, but the fundamental principle of keeping expensive work off the rendering path remains the same.

The most common performance problem is unnecessary re-renders. By default, when a parent component re-renders, all its children re-render too, even if their props haven't changed. This cascading behavior is harmless at small scales but becomes devastating in complex screens with dozens of components.

React.memo, useCallback, and useMemo are your primary weapons. Understanding when—and when not—to use them is the difference between genuine optimization and premature optimization theater.

Preventing Re-renders with React.memo

// ❌ Bad: Card re-renders every time the parent updates,
//    even if title and onPress haven't changed
const Card = ({ title, onPress }) => {
  console.log('Card rendered:', title);
  return (
    <TouchableOpacity onPress={onPress}>
      <Text>{title}</Text>
    </TouchableOpacity>
  );
};
 
// ✅ Good: Card only re-renders when its props actually change
const Card = React.memo(({ title, onPress }) => {
  console.log('Card rendered:', title);
  return (
    <TouchableOpacity onPress={onPress}>
      <Text>{title}</Text>
    </TouchableOpacity>
  );
});
 
// CRITICAL: The parent must also stabilize the onPress reference.
// Without useCallback, a new function is created on every render,
// which makes React.memo's equality check always fail.
const ParentScreen = () => {
  const [count, setCount] = useState(0);
 
  // ❌ New function reference on every render — breaks React.memo
  // const handlePress = () => console.log('pressed');
 
  // ✅ Stable reference — Card won't re-render due to onPress changing
  const handlePress = useCallback(() => {
    console.log('pressed');
  }, []); // Empty deps: created once and reused forever
 
  return (
    <View>
      <Button onPress={() => setCount(c => c + 1)} title="Increment" />
      <Card title="Item" onPress={handlePress} />
      {/* Now, pressing the button increments count but Card doesn't re-render */}
    </View>
  );
};

Memoizing Expensive Computations with useMemo

const ProductList = ({ products, searchQuery, selectedCategory }) => {
  // ❌ Filtering and sorting runs on every render — even when selectedCategory
  //    changes but searchQuery doesn't
  // const filtered = products
  //   .filter(p => p.name.includes(searchQuery))
  //   .sort((a, b) => a.price - b.price);
 
  // ✅ Only recomputes when products or searchQuery changes
  const filtered = useMemo(() => {
    console.log('Computing filtered products...');
    return products
      .filter(p => p.name.toLowerCase().includes(searchQuery.toLowerCase()))
      .sort((a, b) => a.price - b.price);
  }, [products, searchQuery]);
  // selectedCategory changes don't trigger this expensive operation
 
  return <FlatList data={filtered} renderItem={/* ... */} />;
};

When NOT to Use React.memo

This is equally important. React.memo adds a props comparison step on every render. For lightweight components, this overhead can actually exceed the cost of the re-render itself. Rules of thumb:

  • Do use React.memo when: the component renders frequently, renders are expensive, or it's a list item rendered dozens of times
  • Don't use React.memo when: the component is trivial, props change on almost every render, or the component rarely re-renders

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Deeply understand React Native's rendering cycle and learn proven techniques to eliminate unnecessary re-renders
✦Master profiling with Flipper and the Hermes Profiler to pinpoint bottlenecks on real devices with precision
✦Walk away with a comprehensive checklist for bundle size reduction, memory leak prevention, and maintaining 60fps animations
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-04-22
The Three Implementation Details That Decide First-Touch Feel in Rork Apps — Animation, Haptics, and Transition Speed
Two apps can offer the same features yet feel completely different. The reason usually comes down to animation, haptics, and transition speed. Here is exactly how to implement each of the three in a Rork-built app, with code you can drop in today.
⬡ Dev Tools2026-09-10
I Added a Dim Screen Button, and iPhones Stayed Dark After the App Was Closed
In expo-brightness, setBrightnessAsync applies only to the current activity on Android, but changes the device brightness itself on iOS. Here is why the cleanup burden falls on one platform only, and a hook that restores brightness through AppState.
⬡ Dev Tools2026-09-04
EAS secret visibility does not keep a value out of your app — deciding prefix and visibility separately
The EXPO_PUBLIC_ prefix decides what ships inside your app; EAS visibility decides who can read it. Why stacking them blanks a value on OTA updates, and how to check your build.