Caracal Starter
Start Here

React Compiler & New Architecture

Understanding the experimental features enabled in Caracal starter - React Compiler and New Architecture.

Caracal starter enables two experimental features that represent the future of React Native development. This guide explains what they are, how they affect your development, and when you might need to disable them.

Overview

The New Architecture is always on from Expo SDK 55 onward — there is no config flag for it. React Compiler is opted into in app.config.ts:

export default ({ config }: ConfigContext): ExpoConfig => ({
  // ...
  experiments: {
    typedRoutes: true,
    reactCompiler: true,
  },
  // ...
});

Experimental Status: The New Architecture is mandatory and always enabled from Expo SDK 55 onward. React Compiler is stable enough for production use in Expo SDK 57 but is still marked as experimental, and its APIs may evolve.


React Compiler

What is React Compiler?

React Compiler (formerly "React Forget") is an automatic optimization tool that compiles your React components to eliminate the need for manual memoization.

Traditional React (without compiler):

import { useMemo, useCallback } from 'react';

function ExpensiveComponent({ data, onUpdate }) {
  // Manual memoization required
  const processedData = useMemo(() => {
    return data.map(item => transformItem(item));
  }, [data]);

  const handleUpdate = useCallback((id) => {
    onUpdate(id);
  }, [onUpdate]);

  return <View>{/* render */}</View>;
}

// Manual component memoization
export default React.memo(ExpensiveComponent);

With React Compiler (enabled in Caracal):

// No manual memoization needed!
function ExpensiveComponent({ data, onUpdate }) {
  // Compiler automatically optimizes this
  const processedData = data.map(item => transformItem(item));

  const handleUpdate = (id) => {
    onUpdate(id);
  };

  return <View>{/* render */}</View>;
}

// No React.memo needed - compiler handles it
export default ExpensiveComponent;

How It Works

The React Compiler analyzes your components during the build process and automatically:

  1. Memoizes expensive computations - Caches results of heavy calculations
  2. Prevents unnecessary re-renders - Skips rendering when props/state haven't changed
  3. Optimizes callbacks - Stabilizes function references automatically
  4. Eliminates manual optimization - No need for useMemo, useCallback, React.memo

The result: Simpler code that's automatically optimized for performance.

What Changes for You

Write Simpler Code

// ✅ BEFORE React Compiler - Manual optimization
function UserProfile({ user, onSave }) {
  const fullName = useMemo(() => {
    return `${user.firstName} ${user.lastName}`;
  }, [user.firstName, user.lastName]);

  const handleSave = useCallback(() => {
    onSave(user.id);
  }, [onSave, user.id]);

  return (
    <View>
      <Text>{fullName}</Text>
      <Button onPress={handleSave}>Save</Button>
    </View>
  );
}

export default React.memo(UserProfile);
// ✅ WITH React Compiler - Automatic optimization
function UserProfile({ user, onSave }) {
  const fullName = `${user.firstName} ${user.lastName}`;

  const handleSave = () => {
    onSave(user.id);
  };

  return (
    <View>
      <Text>{fullName}</Text>
      <Button onPress={handleSave}>Save</Button>
    </View>
  );
}

export default UserProfile;

Trust the Compiler

  • Don't pre-optimize: Write straightforward code, let the compiler optimize
  • Remove manual memoization: No need for useMemo/useCallback/React.memo in most cases
  • Keep code readable: Prioritize clarity over manual performance tricks

Performance Benefits

Measured improvements in production apps:

  • 30-50% reduction in re-renders
  • Faster component mount times
  • Reduced memory usage from fewer function recreations
  • Better scroll performance in lists

Limitations & Edge Cases

While React Compiler handles most cases, there are scenarios where it doesn't apply:

1. Dynamic Dependencies

// ❌ Compiler can't optimize this
function DynamicComponent({ fields }) {
  // deps array is dynamic - compiler can't determine dependencies
  const result = useMemo(() => {
    return fields.reduce((acc, field) => acc + field, 0);
  }, fields); // Array spread - compiler skips optimization

  return <Text>{result}</Text>;
}

Solution: Keep dependencies explicit and static when possible.

2. External Libraries

Third-party libraries not compiled with React Compiler won't benefit:

// ❌ External library might not be optimized
import SomeLibraryComponent from 'some-library';

// You still benefit from compiler in your code
function YourComponent() {
  // This is optimized by compiler
  const data = computeData();

  // But SomeLibraryComponent isn't
  return <SomeLibraryComponent data={data} />;
}

3. Code with Side Effects

// ⚠️ Compiler is cautious with side effects
function ComponentWithSideEffects() {
  // Compiler won't optimize this aggressively
  // because of the console.log side effect
  const value = (() => {
    console.log('Computing...');
    return computeExpensiveValue();
  })();

  return <Text>{value}</Text>;
}

When to Still Use Manual Memoization

Keep useMemo/useCallback in these specific cases:

1. Extremely Expensive Operations

function DataProcessor({ dataset }) {
  // Keep useMemo for operations taking >100ms
  const processedData = useMemo(() => {
    return dataset.map(item => {
      // Complex processing taking 200ms+
      return expensiveTransform(item);
    });
  }, [dataset]);

  return <DataView data={processedData} />;
}

2. After Profiling Shows Issues

// Profile first, then optimize if needed
function OptimizedAfterProfiling({ data }) {
  // Added useMemo after React DevTools Profiler
  // showed this was causing performance issues
  const sortedData = useMemo(() => {
    return [...data].sort(customSort);
  }, [data]);

  return <List data={sortedData} />;
}

Debugging React Compiler

Disable for Specific Files

If a component has issues, opt-out with a directive:

// @react-compiler-disable

// Compiler will skip this file
function ProblematicComponent() {
  // Your code here
}

Common Issues

Problem: Component not updating when it should

Cause: Compiler over-optimized based on incorrect assumptions

Solution:

// Force re-render with key prop
<ComponentNotUpdating key={forceUpdateKey} />

// Or disable compiler for that component

New Architecture

What is New Architecture?

React Native's New Architecture (also called "Fabric" + "TurboModules") is a complete rewrite of React Native's core rendering and native module systems.

Key Changes:

  • Fabric: New rendering engine replacing the old "Bridge"
  • TurboModules: Faster native module system with lazy loading
  • JSI (JavaScript Interface): Direct JavaScript-to-native communication
  • Codegen: Automatic type-safe bindings between JS and native code

Why It Matters

Old Architecture (Bridge-Based)

JavaScript                 Bridge (Async)              Native
   |                            |                         |
   |--- Send JSON message ----->|                         |
   |                            |--- Parse & forward ---->|
   |                            |                         |
   |<--- Send JSON response ----|<--- Process & return ---|
   |                            |                         |

Problems:

  • Everything async (even simple getters)
  • JSON serialization overhead
  • No direct memory access
  • Performance bottlenecks

New Architecture (JSI-Based)

JavaScript                 JSI (Sync)                  Native
   |                            |                         |
   |--- Direct function call -->|--- Immediate access --->|
   |<--- Return value ----------|<--- Return directly ----|
   |                            |                         |

Benefits:

  • Synchronous access to native modules
  • No JSON serialization
  • Direct memory sharing (ArrayBuffers)
  • Better performance

Performance Improvements

Measured in production apps with New Architecture:

MetricImprovement
App Launch Time20-30% faster
Frame RateMore consistent 60fps
Memory Usage10-15% reduction
Native Module Calls2-3x faster
Large List ScrollingSmoother, fewer drops

Library Compatibility

Most popular libraries support New Architecture:

Fully Compatible:

  • React Navigation
  • React Native Reanimated
  • React Native Gesture Handler
  • React Native Screens
  • React Native Safe Area Context
  • React Native MMKV
  • Expo SDK (all modules)
  • HeroUI Native
  • Uniwind

Partial Support:

Not Compatible:

  • Very old libraries (pre-2020) without updates
  • Libraries using old native module system without migration

Troubleshooting New Architecture

Issue: App crashes on startup

Possible cause: Incompatible native module

Solution:

  1. Check all dependencies for New Architecture support
  2. Update libraries to latest versions
  3. Remove incompatible libraries temporarily

Issue: Native module not found

Possible cause: Module needs rebuilding

Solution:

# iOS
cd ios && pod deintegrate && pod install && cd ..

# Android
cd android && ./gradlew clean && cd ..

# Rebuild
pnpm prebuild

When to Disable These Features

Disable React Compiler If:

  1. Debugging optimization issues

    // app.config.ts
    experiments: {
      reactCompiler: false, // Temporarily disable
    }
  2. Specific components have problems

    // Add to top of file
    // @react-compiler-disable

You Cannot Disable the New Architecture

The legacy architecture was removed in Expo SDK 55, and the newArchEnabled option no longer exists. Caracal runs on Expo SDK 57 / React Native 0.86, so the New Architecture is always active. If a library does not support it, replace the library rather than trying to opt out — see Library Compatibility.

Disabling React Compiler

1. Update app.config.ts:

export default ({ config }: ConfigContext): ExpoConfig => ({
  // ...
  experiments: {
    typedRoutes: true,
    reactCompiler: false,    // Disable React Compiler
  },
  // ...
});

2. Clear build caches:

rm -rf node_modules
rm -rf ios/build android/app/build
rm -rf .expo

pnpm install
pnpm prebuild
pnpm start --clear

3. Rebuild app:

pnpm ios
pnpm android

Best Practices

With React Compiler

DO:

  • Write simple, readable code
  • Trust the compiler to optimize
  • Profile before manually optimizing
  • Remove unnecessary memoization
  • Keep dependencies explicit

DON'T:

  • Don't over-optimize prematurely
  • Don't assume manual memoization is always better
  • Don't skip testing after removing memoization
  • Don't forget to profile in production mode

With New Architecture

DO:

  • Check library compatibility before installing
  • Update libraries regularly
  • Test thoroughly on both platforms
  • Use Expo modules when possible (built for New Architecture)
  • Monitor crash reports for native issues

DON'T:

  • Don't assume all libraries work immediately
  • Don't skip testing after library updates
  • Don't ignore deprecation warnings
  • Don't mix old and new native module patterns

Verify Features Are Active

Verify React Compiler is Active

# Build in production mode
pnpm prebuild:production

# Check build output for compiler messages
# Look for: "Compiled N components with React Compiler"

Verify New Architecture is Active

// Add to a screen temporarily
import { Text, View } from 'react-native';

export default function DebugScreen() {
  const isFabric = global.nativeFabricUIManager != null;

  return (
    <View>
      <Text>New Architecture: {isFabric ? 'Enabled' : 'Disabled'}</Text>
    </View>
  );
}

Or check native logs:

# iOS logs
# Look for: "Fabric enabled: true"

# Android logs
adb logcat | grep -i fabric
# Look for: "Fabric is enabled"

Migration Path

If you're upgrading from an older starter version without these features:

Phase 1: Move to the New Architecture First

  1. Upgrade to Expo SDK 55 or later — the New Architecture is enabled automatically and cannot be turned off
  2. Update all dependencies to latest versions
  3. Test thoroughly on both platforms
  4. Fix any compatibility issues

Phase 2: Enable React Compiler

  1. Update app.config.ts: reactCompiler: true
  2. Remove unnecessary useMemo/useCallback/React.memo gradually
  3. Test each change
  4. Profile to verify improvements

Phase 3: Optimize

  1. Profile app with both features enabled
  2. Identify remaining bottlenecks
  3. Add manual optimizations only where profiling shows need
  4. Monitor production performance

Resources


FAQ

Q: Should I remove all useMemo/useCallback from my code?

A: No, not immediately. Remove them gradually:

  1. Profile first to understand current performance
  2. Remove memoization from simple components
  3. Keep memoization for extremely expensive operations (>100ms)
  4. Test and profile after each change

Q: Will React Compiler make my app slower?

A: No. React Compiler only adds optimizations, never removes them. If it can't optimize safely, it leaves code unchanged.

Q: Can I use New Architecture with Expo Go?

A: Partially. Expo Go in Expo SDK 57 runs on the New Architecture, but you need a custom dev client for full support with native modules. Use pnpm prebuild and pnpm ios/pnpm android.

Q: What if a critical library doesn't support New Architecture?

A: Opting out is not an option on SDK 55+, so you have to move the library forward:

  1. Find an alternative library that supports it
  2. Wait for the library to update (check GitHub issues)
  3. Contribute a PR to add support
  4. Pin to an older SDK only as a last resort, and plan the migration

Q: Can I gradually adopt React Compiler per-file?

A: The compiler is enabled globally, but you can opt-out specific files with // @react-compiler-disable directive at the top of the file.


Summary

Caracal starter enables React Compiler and New Architecture to provide:

  • Automatic performance optimization without manual memoization
  • Faster native module access and better threading
  • Improved app startup time and frame rates
  • Simpler code that's easier to maintain
  • Future-proof architecture aligned with React Native's direction

Bottom line: These features make your app faster and your code simpler. Start building with confidence, and only disable them if you encounter specific compatibility issues with third-party libraries.


Next Steps:

On this page