◉RORK LABJP
●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first
Articles/Dev Tools
⬡ Dev Tools/2026-07-10Advanced

Adding React Compiler to Expo Let Me Delete 41 Hand-Written memo Calls

I enabled React Compiler on Rork-generated React Native screens and measured the rerender counts with Profiler. Here is how I decided which memo and useCallback calls were safe to delete, how to find the components the compiler bailed out on, and how to catch regressions in CI.

React CompilerExpo213React Native238Performance25Rork577

✦ Premium Article

Tapping the favorite button on a single thumbnail made the whole wallpaper grid flicker. I opened the Profiler and found that all 24 visible cells had rerendered. Exactly one cell had changed.

The cause was not mysterious. The onToggleFavorite handler that Rork had generated was recreated on every render, which rebuilt the entire renderItem for the FlatList.

So I did what everyone does. Wrap it in useCallback. Wrap the cell in React.memo. Stare at the dependency array. I have lost count of how many times I repeated that ritual across six apps. If I am honest, I have introduced more "this screen stopped updating" bugs through mistaken dependency arrays than I ever gained in milliseconds.

That fatigue is why I finally sat down with React Compiler.

What I Actually Tested

The subject is one of the wallpaper apps I run as a solo developer. Three React Native screens built on Expo — grid, detail, settings — originally generated by Rork. Between them they contained 63 hand-written React.memo, useCallback, and useMemo calls.

I wanted answers to three questions.

  1. With React Compiler enabled, how much do rerender counts actually drop?
  2. Are the hand-written memo calls safe to delete? Where does deleting them make things slower?
  3. Can I find the components the compiler gave up on mechanically, rather than by reading every file?

The short version: rerenders on the main interaction dropped from 24 to 2, I deleted 41 of the 63 memo calls, and the remaining 22 each had a concrete reason to stay.

Setup Is One Babel Line, With Two Preconditions

Turning it on in Expo is brief.

npx expo install babel-plugin-react-compiler
// babel.config.js
module.exports = function (api) {
  api.cache(true);
  return {
    presets: [["babel-preset-expo", { "react-compiler": true }]],
  };
};

Passing react-compiler: true to babel-preset-expo lets the preset wire everything up. If you instead add the plugin to plugins by hand, you can end up applying it twice alongside the preset's React Native specific adjustments. I broke a build exactly this way once. Go through the preset.

Two preconditions matter.

Precondition 1: New Architecture must be on. The compiler technically runs on the old architecture, but the rendering behavior it assumes — automatic batching, the re-entrancy that Concurrent rendering brings — only lines up under New Architecture. With the old architecture still in place, the rerender count that should have fallen to 2 stalled at 5. My staged migration notes are in the New Architecture migration writeup.

Precondition 2: Clear the Metro cache. Without npx expo start --clear, you keep serving the pre-transform bundle. Most "I enabled it and nothing changed" reports are this. I covered Metro's caching quirks separately in the Fast Refresh troubleshooting notes.

✦

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
✦A reproducible way to measure rerender counts before and after enabling React Compiler
✦Clear criteria for which hand-written memo and useCallback calls are safe to remove and which are not
✦How to find components the compiler silently bailed out on, using ESLint and build output rather than eyeballing
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 $15 for lifetime access
View Membership →

Related Articles

⬡ 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.
⬡ Dev Tools2026-08-22
Every bulk replace exited zero. The damage was in the lines I did not delete
Run a bulk replace over generated code and the breakage lands on the neighbouring lines, not the matched ones. Here is what broke in a live project, and a dependency-free guard that checks the invariants a replace must preserve.
⬡ Dev Tools2026-08-02
When Two-Character Queries Silently Return Nothing: Measuring Japanese Search Indexes in an Expo App
SQLite FTS5's trigram tokenizer returns zero rows for Japanese queries shorter than three characters, without raising anything. I benchmarked linear scan, a bigram inverted index, and FTS5 over a 20,000-item catalog to find the real threshold.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links