You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Tracking] Use React Compiler to enforce the Rules of React #68765
Background: The React Compiler is meant to automatically optimize React code with invisible React.memo, useMemo, and useCallback calls. This in theory eliminates the need to use these React constructs in the codebase.
However, we see many examples where performance can be improved with useMemo. It's unclear when React Compiler will "do it's thing" and when it won't.
Furthermore, we have many many components and hooks throughout the codebase the violate the Rules of React.
The React Compiler has come a long way since this issue was first created, and it's now much more tightly integrated with lint rules in eslint-plugin-react-hooks which are designed precisely to help enforce the Rules of React and to teach developers how to write better React Code in the process.
Problem:
(done) Update eslint-plugin-react-hooks and enable all rules globally. See what errors crop up. There were 559.
(done) Make them all warnings in the global ESLint config, and set --max-warnings accordingly so that npm run lint passes on main.
Create issues to fix all the react-hooks lint rules, work through them in batches. At the end of this effort:
All files should be compiled with React Compiler
All files should be compliant with the Rules of React (insofar as the React Compiler can currently enforce that)
(optional) Create issues to start removing all unnecessary manual memoization (to simplify/reduce the codebase, and potentially see performance gains)
hey @roryabraham sorry for the late update, i was busy with other PRs. I wasn't able to work on this yet, but i'd like to start either today or tomorrow. Lmk if this needs to be handled more urgently, maybe i can get someone from Margelo to work on it!
@MelvinBot how many components and hooks do we have total, and how many do not compile with React Compiler? Focus only on the Rust React Compiler now, since soon that will be the only one we have
We have 4,895 components and hooks. 85 of them (1.7%) do not compile with the OXC (Rust) React Compiler.
Total
Don't compile
Components
3,740
66
Hooks
1,155
19
Total
4,895
85
Why they fail (first OXC error for each function):
64: Cannot access refs during render
8: Existing memoization could not be preserved
4: This value cannot be modified
7: Compiler limitations (getters in object literals, try/finally, ??=, dynamic import(), and others)
2: Internal compiler invariant errors (usePermissions, web useSingleExecution)
Note: react-compiler-compliance-check flags only 71 of these files. It runs OXC with panicThreshold: 'critical_errors', so memoization-preservation and limitation bailouts pass the check. The web build still ships those 14 functions uncompiled.
How I counted
Scope: every .ts/.tsx/.js file under src/, excluding tests/ (8,118 files), at App 6eb1f75. Platform variants (.native.tsx, .web.tsx) count as separate functions. A "hook" is a function whose name starts with use.
Inventory: the functions React Compiler treats as components or hooks in infer mode. I took them from the babel-plugin-react-compiler logger (CompileSuccess and CompileError events). OXC did not compile any function outside this inventory, so both compilers agree on the set.
OXC result:oxc-transform-react 0.148.0 with the production web config (oxcReactCompilerConfig.js, panicThreshold: 'none'), plus the gating option. Gating wraps every function OXC compiles in gate() ? compiled : original, so counting the wrappers gives an exact per-function result. In every file, the memo-cache count matched the ungated build.
Reasons: for each failing function, I re-ran OXC with panicThreshold: 'all_errors' and recorded the first diagnostic.
No function uses "use no memo". All 85 OXC failures also fail under Babel.
Activity
roryabraham commentedon Aug 19, 2025
Similar PR check was added in the past: #49397
roryabraham commentedon Aug 26, 2025
any update @chrispader
roryabraham commentedon Aug 26, 2025
@mountiny moved this to
MEDIUM, as that feels more appropriate thanHIGHto me for this particular issuemountiny commentedon Aug 28, 2025
Cool, I'm fine with that. Thanks for the ping!
roryabraham commentedon Sep 3, 2025
@chrispader any update here?
chrispader commentedon Sep 8, 2025
hey @roryabraham sorry for the late update, i was busy with other PRs. I wasn't able to work on this yet, but i'd like to start either today or tomorrow. Lmk if this needs to be handled more urgently, maybe i can get someone from Margelo to work on it!
roryabraham commentedon Sep 9, 2025
No worries @chrispader. Do you think you can get a PR up this week?
chrispader commentedon Sep 10, 2025
yes, i'll have a PR up by the end of tomorrow!
347 remaining items
roryabraham commentedon Oct 20, 2026
@MelvinBot how many components and hooks do we have total, and how many do not compile with React Compiler? Focus only on the Rust React Compiler now, since soon that will be the only one we have
MelvinBot commentedon Oct 20, 2026
We have 4,895 components and hooks. 85 of them (1.7%) do not compile with the OXC (Rust) React Compiler.
Why they fail (first OXC error for each function):
try/finally,??=, dynamicimport(), and others)usePermissions, webuseSingleExecution)Note:
react-compiler-compliance-checkflags only 71 of these files. It runs OXC withpanicThreshold: 'critical_errors', so memoization-preservation and limitation bailouts pass the check. The web build still ships those 14 functions uncompiled.How I counted
.ts/.tsx/.jsfile undersrc/, excludingtests/(8,118 files), at App6eb1f75. Platform variants (.native.tsx,.web.tsx) count as separate functions. A "hook" is a function whose name starts withuse.infermode. I took them from thebabel-plugin-react-compilerlogger (CompileSuccessandCompileErrorevents). OXC did not compile any function outside this inventory, so both compilers agree on the set.oxc-transform-react0.148.0 with the production web config (oxcReactCompilerConfig.js,panicThreshold: 'none'), plus thegatingoption. Gating wraps every function OXC compiles ingate() ? compiled : original, so counting the wrappers gives an exact per-function result. In every file, the memo-cache count matched the ungated build.panicThreshold: 'all_errors'and recorded the first diagnostic."use no memo". All 85 OXC failures also fail under Babel.All 85 components and hooks that don't compile
Expensify.tsxGlobalModals.tsxcomponents/AnimatedSubmitButton/index.tsxcomponents/AttachmentPicker/index.native.tsxcomponents/AttachmentPicker/index.tsxcomponents/Attachments/AttachmentCarousel/AttachmentCarouselView/index.tsxcomponents/AutoCompleteSuggestions/AutoCompleteSuggestionsPortal/TransparentOverlay/TransparentOverlay.tsxcomponents/Composer/implementation/index.native.tsxcomponents/Composer/implementation/index.tsxcomponents/ConnectToNetSuiteFlow/index.tsxcomponents/DatePicker/index.tsxcomponents/DisplayNames/DisplayNamesWithTooltip.tsxcomponents/DragAndDrop/NoDropZone/index.tsxcomponents/DragAndDrop/Provider/index.tsxcomponents/DropZone/DropZoneWrapper.tsxcomponents/EmojiPicker/EmojiPicker.tsxcomponents/EmojiPicker/EmojiPickerMenu/index.native.tsxcomponents/EmojiPicker/EmojiPickerMenu/index.tsxcomponents/FilePicker/index.native.tsxcomponents/FilePicker/index.tsxcomponents/Form/FormProvider.tsxcomponents/Form/InputWrapper.tsxcomponents/Hoverable/ActiveHoverable.tsxcomponents/Hoverable/index.tsxcomponents/Image/index.tsxcomponents/ImportSpreadsheet.tsxcomponents/KYCWall/BaseKYCWall.tsxcomponents/Modal/ReanimatedModal/Container/index.web.tsxcomponents/MoneyRequestAmountInput.tsxcomponents/MultiGestureCanvas/index.tsxcomponents/Pressable/GenericPressable/implementation/BaseGenericPressable.tsxcomponents/Reactions/AddReactionBubble.tsxcomponents/Reactions/MiniQuickEmojiReactions.tsxcomponents/ReportActionItem/ExportWithDropdownMenu.tsxcomponents/ScreenWrapper/ScreenWrapperContainer.tsxcomponents/Search/index.tsxcomponents/SwipeableView/index.native.tsxcomponents/TextInput/BaseTextInput/implementation/index.native.tsxcomponents/TextInput/BaseTextInput/implementation/index.tsxcomponents/Tooltip/BaseGenericTooltip/index.native.tsxcomponents/Tooltip/BaseGenericTooltip/index.tsxcomponents/Tooltip/PopoverAnchorTooltip.tsxcomponents/VideoPlayer/BaseVideoPlayer.tsxhooks/useCurrentReportID.tsxhooks/useSidebarOrderedReports.tsxlibs/Navigation/AppNavigator/AuthScreensInitHandler.tsxlibs/Navigation/AppNavigator/DelegatorConnectGate.tsxpages/MissingPersonalDetails/subPages/Address.tsxpages/ReimbursementAccount/ReimbursementAccountPage.tsxpages/TransactionDuplicate/DynamicConfirmationPage.tsxpages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsxpages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsxpages/inbox/report/ReportActionCompose/ComposerWithSuggestions.tsxpages/inbox/report/ReportActionCompose/SuggestionEmoji.tsxpages/inbox/report/ReportActionCompose/SuggestionMention.tsxpages/inbox/report/ReportActionItemMessageEdit.tsxpages/inbox/report/withReportOrNotFound.tsxpages/iou/request/step/IOURequestStepConfirmation/index.tsxpages/settings/AboutPage/AboutPage.tsxpages/settings/Profile/Contacts/DynamicContactMethodDetailsPage.tsxpages/settings/Profile/Contacts/DynamicNewContactMethodPage.tsxpages/settings/Wallet/AddDebitCardPage.tsxpages/workspace/WorkspacePageWithSections.tsxpages/workspace/members/DynamicWorkspaceOwnerChangeErrorPage.tsxpages/workspace/members/DynamicWorkspaceOwnerChangeSuccessPage.tsxpages/workspace/members/ImportedMembersConfirmationPage.tsxcomponents/ActionSheetAwareScrollView/useActionSheetAwareScrollViewRef.tscomponents/MultifactorAuthentication/Context/usePromptContent.tshooks/useAutoCreateTrackWorkspace.tshooks/useCancellationType.tshooks/useFirstRenderRoute.tshooks/useHtmlPaste/index.tsBinaryExpressioncannot be safely reorderedhooks/useInitial.tshooks/useIsOwnWorkspaceChatRef.tshooks/useNewTransactions.tshooks/usePaginatedReportActions.tshooks/usePaymentOptions.tshooks/usePermissions.tshooks/usePrevious.tshooks/usePreviousDefined.tshooks/useSearchBulkActions.tshooks/useSelectedTransactionsActions.tshooks/useSingleExecution/index.native.tshooks/useSingleExecution/index.tspages/inbox/report/useReportUnreadMessageScrollTracking.tsview run
roryabraham commentedon Oct 20, 2026
Posted proposal related to this: https://expensify.slack.com/archives/C01GTK53T8Q/p1792522792041069
roryabraham commentedon Oct 20, 2026
My plan is to implement this proposal, then hopefully close this issue as completed.
melvin-bot commentedon Oct 23, 2026
@chrispader, @roryabraham, @situchan Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
melvin-bot commentedon Oct 27, 2026
@chrispader, @roryabraham, @situchan 6 days overdue. This is scarier than being forced to listen to Vogon poetry!
roryabraham commentedon Oct 27, 2026
#104514 is merged on main, which both:
Once that makes it to production, imo we can close this issue out.
chrispader commentedon Oct 28, 2026
Yes sounds good! Awesome work @roryabraham 🙌🏼
roryabraham commentedon Oct 29, 2026
Closing this as complete 🎉
roryabraham commentedon Oct 29, 2026
posted a lil announcement about this: https://expensify.slack.com/archives/C01GTK53T8Q/p1793308633097789