React NativeReact CompilerPerformanceExpo

React Compiler in React Native and Expo: Setup, Benefits, and Gotchas

Enable the React Compiler in Expo and React Native for automatic memoization. Learn the app.json setup, what it optimizes, when you can delete useMemo, useCallback, and React.memo, how to opt out with 'use no memo', and how to roll it out safely.

DeveloperMill3 min read

Every React Native developer has written useCallback "just in case" to stop a list row from re-rendering. The React Compiler makes most of that unnecessary. It analyzes your components at build time and memoizes them automatically, so the right things re-render and nothing else does. It's stable, it works with React Native, and Expo makes enabling it a one-line change.

What the React Compiler does

The compiler is a Babel plugin that rewrites your components so that:

  • JSX, computed values, and callbacks are cached and only recomputed when their inputs change.
  • Child components skip re-rendering when their props haven't changed, without React.memo.
  • Inline functions and objects stay referentially stable, which keeps FlatList and FlashList rows from re-rendering.

You write plain React, and the output behaves as if you had wrapped everything correctly in useMemo and useCallback, without the bugs from missing dependencies.

Enable it in Expo

On Expo SDK 54 and later, Babel is configured for you. Turn it on in app.json:

{
  "expo": {
    "experiments": {
      "reactCompiler": true
    }
  }
}

Restart the bundler with a clean cache:

npx expo start --clear

Check your codebase first

Before turning it on in a large app, run the health check:

npx react-compiler-healthcheck@latest

It reports how many components the compiler can optimize and flags patterns it can't. On SDK 55+, React Compiler lint rules are included in eslint-config-expo, so npx expo lint shows problems inline.

Before and after

Before, manual memoization everywhere:

const ProductRow = React.memo(function ProductRow({ product, onAdd }: Props) {
  return <Row title={product.title} onPress={() => onAdd(product.id)} />
})

export function ProductList({ products }: { products: Product[] }) {
  const addToCart = useCart((s) => s.add)
  const sorted = useMemo(() => [...products].sort(byPrice), [products])
  const handleAdd = useCallback((id: string) => addToCart(id), [addToCart])

  return (
    <FlashList
      data={sorted}
      renderItem={({ item }) => <ProductRow product={item} onAdd={handleAdd} />}
    />
  )
}

After, plain React. The compiler handles it:

function ProductRow({ product, onAdd }: Props) {
  return <Row title={product.title} onPress={() => onAdd(product.id)} />
}

export function ProductList({ products }: { products: Product[] }) {
  const addToCart = useCart((s) => s.add)
  const sorted = [...products].sort(byPrice)

  return (
    <FlashList
      data={sorted}
      renderItem={({ item }) => <ProductRow product={item} onAdd={addToCart} />}
    />
  )
}

There's less code, no dependency arrays to get wrong, and the same or better performance.

Opt out a component

If a component misbehaves, add "use no memo" as the first statement to skip it:

function LegacyChart(props: ChartProps) {
  "use no memo"
  // ...
}

Use it as a temporary escape hatch while you fix the underlying issue.

What breaks the compiler (the Rules of React)

The compiler assumes your components are pure. Code that worked "by accident" can behave differently:

  • Mutating props, state, or objects during render, like items.push(x) inside the component body. Create new arrays instead.
  • Reading or writing ref.current during render. Do it in effects or event handlers.
  • Conditional hooks or hooks called in loops.
  • Relying on a component re-rendering to pick up a mutable global value.

If the compiler can't prove a component is safe, it skips it rather than breaking it, so the main risk is in code that violates the rules in ways it can't detect.

Should you remove existing useMemo and useCallback?

  • New code: don't add them by default.
  • Existing code: leave them. They're harmless with the compiler on. Remove them gradually in files you're already editing.
  • Keep them for values that must stay stable for third-party APIs or effect dependencies, until you've verified behavior.

Measuring the impact

  • Use React Native DevTools' profiler to compare re-render counts before and after.
  • Watch for smoother list scrolling and typing in forms (where wasted re-renders hurt most).
  • Test a release build. Dev mode adds overhead that hides real gains.

Pairs well with

The compiler fixes wasted re-renders. It doesn't fix slow lists or JS-thread animations. Combine it with FlashList v2 for recycling, Reanimated 4 for UI-thread animation, and Zustand selectors for focused subscriptions. Our components are written as plain, rules-of-React-friendly code, so they work with the compiler enabled.

Frequently asked questions

Does the React Compiler work with React Native?

Yes. React Compiler v1.0 (stable since October 2025) works with both React and React Native. In Expo SDK 54 and later, Babel is configured automatically — you just enable it with `experiments.reactCompiler: true` in app.json.

Do I still need useMemo and useCallback with the React Compiler?

Mostly no. The compiler automatically memoizes components, values, and callbacks at build time. You can stop adding new useMemo, useCallback, and React.memo calls. Keep existing ones until you've verified behavior, and keep them where a value must be stable for an effect dependency that the compiler can't analyze.

How do I disable the React Compiler for one component?

Add the `"use no memo"` directive as the first statement in the component's function body (or at the top of a file). The compiler then skips that component, which is useful for debugging or for code that breaks the Rules of React.

Is the React Compiler safe to enable in an existing app?

Usually, if your code follows the Rules of React. Run `npx react-compiler-healthcheck@latest` first, fix lint errors from the React Compiler ESLint rules (included in eslint-config-expo on SDK 55+), and test your release build — especially screens that mutate objects or read refs during render.

Build it faster with a template

Production-ready Expo React Native templates with the patterns from this post already wired up.

Browse templates