Turn on the React Compiler (Where Supported) to Reduce Re-render Costs

React performance problems in large React Native and Expo projects almost always trace back to the same root cause: wasted re-renders. As component trees grow, the cost of unnecessary reconciliation quietly compounds. Developers respond with useMemo, useCallback, React.memo, and hand-rolled optimizations, often inconsistently, sometimes incorrectly, and always at a cognitive cost.

The React Compiler changes this equation. When enabled, it can automatically infer memoization boundaries, drastically reducing re-renders without manual intervention. Despite this, it remains significantly underused in React Native and Expo projects.

In this article I explain why enabling the React Compiler is one of the highest-leverage performance improvements you can make today, how it works, where it is supported, and what you should do as a developer to take advantage of it.


The Real Cost of Re-renders in RN and Expo

In React Native, re-renders are more expensive than in web React:

A single state change near the root can easily trigger hundreds of component re-executions, even when most outputs don’t change.

The traditional mitigation strategy looks like this:

const Item = React.memo(function Item({value}) {
  return <Text>{value} < /Text>
});

const List = ({items}) => {
  const renderItem = useCallback(
    ({item}) => <Item value={item.value}/>,
    []
  );

  return <FlatList data={items} renderItem={renderItem}/>;
};

This works, but it comes with problems:

Most teams either over-memoize (wasting memory) or under-memoize (wasting CPU). The React Compiler exists to remove this tradeoff.


What the React Compiler Actually Does

The React Compiler is a build-time optimization that analyzes your components and automatically memoizes values, callbacks, and subtrees when it is safe to do so.

Key properties:

In other words: React becomes closer to how developers assume it already works.


Why This Is Still Underused in React Native and Expo

Despite its benefits, many RN and Expo projects have not enabled the React Compiler. The reasons are mostly practical, not technical:

  1. Tooling lag: RN projects often trail web React in adopting new compiler features.
  2. Fear of instability: Mobile teams are more conservative about build pipelines.
  3. Legacy Babel configurations: Many projects still use complex, brittle Babel setups.
  4. Habitual over-memoization: Teams have internalized manual memoization as “best practice.”

The result: teams continue paying re-render costs they no longer need to pay.

This is a mistake. On large trees, the compiler routinely removes 30–70% of wasted renders with zero runtime cost.


Where the React Compiler Is Supported

As of now, the React Compiler is supported in:

Expo projects are actually well-positioned to adopt it because Expo already standardizes much of the build chain.


Enabling the React Compiler

1. Update React

You must be on a compatible React version:

{
  "dependencies": {
    "react": "^19.0.0",
    "react-native": ">=0.75"
  }
}

2. Enable the Babel Plugin

Add the compiler plugin to your Babel config:

module.exports = {
  presets: ['babel-preset-expo'],
  plugins: [
    ['react-compiler', {target: 'react-native'}]
  ],
};

For non-Expo RN projects, ensure Metro uses this Babel config consistently.


Removing Manual Memoization (Yes, Really)

Once the compiler is enabled, you should actively remove unnecessary memoization.

Before:

const total = useMemo(() => {
  return items.reduce((sum, i) => sum + i.price, 0);
}, [items]);

const onPress = useCallback(() => {
  submit(total);
}, [total]);

After:

const total = items.reduce((sum, i) => sum + i.price, 0);

const onPress = () => {
  submit(total);
};

The compiler will memoize both total and onPress if (and only if) doing so prevents wasted work.

This leads to:

Manual memoization should now be the exception, not the rule.


Large Component Trees Benefit the Most

Consider a common pattern in RN apps:

<App>
  <Navigation>
    <Screen>
      <Header/>
      <Content>
        <Feed>
          <Post/>
          <Post/>
          <Post/>
        </Feed>
      </Content>
    </Screen>
  </Navigation>
</App>

A state change in Screen often re-renders the entire subtree—even if only Header actually changed.

With the compiler enabled:

This is particularly impactful in:


When You Still Need Manual Control

The React Compiler is powerful, but not magic. You still need to think about:

If a callback is passed into a non-React system that depends on identity stability, explicit memoization can still be appropriate.

The difference is intent: you now optimize only when necessary.


What Developers Should Do Today

  1. Enable the React Compiler in new projects by default
  2. Audit existing projects for unnecessary memoization
  3. Delete useMemo / useCallback that exist “just in case”
  4. Measure re-renders before and after
  5. Treat manual memoization as a performance escape hatch

If you are maintaining a large RN or Expo codebase and have not tried the compiler, you are almost certainly leaving performance on the table.


Final Words

Manual memoization was always a tax on developer productivity. The React Compiler finally removes that tax.

In React Native and Expo projects, where render costs are higher and trees are deeper, not enabling it is a self-inflicted performance penalty. Turn it on where supported, simplify your components, and let the compiler do the work it was designed to do.

This is one of the rare cases where the best performance move is also the cleanest architectural choice.