Skip to content

[compiler] Rewrite React Compiler Docs #7868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 21, 2025
Merged

[compiler] Rewrite React Compiler Docs #7868

merged 2 commits into from
Jul 21, 2025

Conversation

poteto
Copy link
Member

@poteto poteto commented Jul 11, 2025

We've received feedback that the compiler docs are difficult to understand and not prominent enough that people don't realize the compiler is a serious project and is near stable.

This PR rewrites the whole compiler doc section, giving it its own category as well as a standalone reference page.

Preview: https://react-dev-git-pr7868-fbopensource.vercel.app/


Stack created with Sapling. Best reviewed with ReviewStack.

Copy link

github-actions bot commented Jul 11, 2025

Size changes

📦 Next.js Bundle Analysis for react-dev

This analysis was generated by the Next.js Bundle Analysis action. 🤖

Five Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load
/404 127.91 KB (🟡 +59 B) 238.35 KB
/500 127.92 KB (🟡 +59 B) 238.36 KB
/[[...markdownPath]] 130 KB (🟡 +233 B) 240.45 KB
/errors 128.16 KB (🟡 +59 B) 238.61 KB
/errors/[errorCode] 128.14 KB (🟡 +59 B) 238.58 KB
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 10% or more, there will be a red status indicator applied, indicating that special attention should be given to this.

@@ -12,6 +12,12 @@ const cachedValue = useMemo(calculateValue, dependencies)

</Intro>

<Note>

[React Compiler](/learn/react-compiler) automatically memoizes values and functions, reducing the need for manual `useMemo` calls. You can use the compiler to handle memoization automatically.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great, was just browsing the docs and surprised it wasn't there


One of the main ways React Compiler can break your app is if your code was written to rely on memoization for correctness. This means your app depends on specific values being memoized to work properly. Since the compiler may memoize differently than your manual approach, this can lead to unexpected behavior like effects over-firing, infinite loops, or missing updates.

Common scenarios where this occurs:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another breaking pattern is when you reference a variable declaration inside a callback that was declared after the callback. This can happen accidentally when writing an especially complex component or hook. That callback won't be memoized by the compiler.

Would you consider this a common scenario?
https://playground.react.dev/#N4Igzg9grgTgxgUxALhAMygOzgFwJYSYAEAQgIYAmAFAJRHAA6xRchYORhAwgDZ5wBrIgF4itEQD56TIrJZsIPBADoeEAOZU0ECDRlEAvvv2tM7IgG1tEADREwCHADEdAXRFEoDgMo4yOBCoABj1MfRhHWGIAHgo8ADdOTF5+AWFgbj5BAwlgawNogHo4+IkmIzDMDGx8QiIAcR1qOkZmU3MrHTsHZzcPLwRff0CQkzYOTNSPcWEpVrl5M0UVNU1rUNkK8MiYGJKklMF0yezc-KKSsswDEAMgA

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is a fun case. Note that you can't memoize such callbacks yourself — adding a useCallback() in the "Bad" example in your playground link would be a TDZ violation when you added the dep on foo:

function Bad() {
  const onClick = useCallback(() => {
    console.log(foo)
  }, [foo /* TDZ violation */ ]);
  
  const [foo, setFoo] = useState(0)

  return <div onClick={onClick}>{foo}</div>
}

We can look into adding linting against this pattern to suggest moving the callback. Note that the compiler will already reject this code if you try to manually memoize it, because the dependency changes later.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider this a common scenario?

Since this pattern doesn't work with manual memoization, this pattern is most likely in code that isn't memoized already. Definitely worth adding validation to help identify opportunities in such code to memoize more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Question:

It may not belong to the "incremental adoption" section, but I wonder if it's worth answering the following question in the adoption plan or somewhere else:

There is a recurring question regarding the best practice for handling existing manual memoization APIs (useMemo, useCallback, memo) in a codebase after the React Compiler has been enabled. This uncertainty is common in discussions about adopting the compiler for large, legacy applications.

To address this, we could add a section to the official documentation, that answers the following:

FAQ: What is the recommended strategy for useMemo, useCallback, and memo in a large and legacy codebase after enabling the React Compiler?

Once the compiler is active, should teams begin a gradual process of removing these manual memoizations?
Our working assumption is that this is the intended outcome, as it aligns with the core purpose of the compiler.

Providing clear, official guidance on this topic will significantly reduce community uncertainty and help teams establish a consistent and effective adoption plan.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry one more question, will useCallback, useMemo, and memo eventually be marked as deprecated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still in the process of figuring out if we can safely deprecate manual memoization. It would be a nice DX win if we can make it happen. But to set expectations, it's better to assume that useMemo, useCallback, and React.memo will stay for the foreseeable future. However, with React Compiler most apps should be able to remove them, so it's possible that as the compiler gets adopted more broadly, these APIs will naturally become less commonplace.

One reason that makes it difficult to just deprecate manual memoization is that apps today may rely on it for "correctness". That means not using memoization purely for performance, but to prevent infinite loops or overfiring effects. This is a bad reason to use manual memoization as it's likely hiding some latent bugs in your app. One way to test this in your app is by codemodding away manual memoization locally in dev. Your app will be slower, but it should not break.

There's a lot more nuances here that our team is currently exploring. When we finish our research on this topic, we will share our findings with the community.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Question:

Is the use of react-compiler-healthcheck recommended at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That package isn't being actively maintained right now so I'm hesitant to document it. You can run the eslint rule as an alternative to get the same information on which files have violations that were detected.

@eppisapiafsl
Copy link

👋 Have a question around the removal of useCallback declaration

  1. Could you confirm how the arrow function will behave after removing the useCallback?

For performance improvement, it's recommended to use the jsx-no-bind to memoize our function and prevent creating a new instance in every re-render

Do we have an equivalent of the rule for the React-compiler, or will it be onPress={() => { some actions}} automatically memoized?

Comment on lines +68 to +69
You can also try removing manual memoization (useMemo, useCallback, memo) from the problematic component to verify that your app works correctly without any memoization. If the bug still occurs when all memoization is removed, you have a Rules of React violation that needs to be fixed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding my thoughts, I recommend separating the concepts of "transition" and "future vision" more clearly in the documents about the React Compiler.

Many developers are now removing these hooks preemptively, which can cause compatibility issues and potential regressions:

  • If the callback is used in useEffect can trigger warnings from the exhaustive-deps-hooks rule.
  • The compiler often indicates when code does not behave as expected. If developers remove memoization hooks without evaluating their impact, projects might experience unforeseen regressions. (This is on the developer side, not really an issue from the compiler)

From what I understand, the long-term goal is to remove the need for hooks such as useCallback, useMemo, and memo. However, unless I am mistaken, the current state does not require developers to remove these hooks immediately.

I know this is optional, but suggesting a formal transition plan will help developers and companies understand the work to do for a smooth transition. For example:

Step 1: Ensure code is fully compliant with the React Compiler.
Step 2: Once Meta officially announces support, remove implementations of useCallback, useMemo, and similar hooks (optionally, developers may do this earlier if they choose).

In theory, that info is already in the doc 😅, but I would centralize it in a single file

Could we clarify this distinction in the documentation? If my understanding is incorrect, I welcome input on the current expectations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks for sharing it @poteto ; it does 👍

poteto added 2 commits July 21, 2025 11:54
Fixes the actual broken links.

```
$ yarn deadlinks

yarn run v1.22.22
$ node scripts/deadLinkChecker.js
Checking 177 markdown files...
Fetched 552 React error codes
Loaded 49 redirects from vercel.json

✓ All 1554 links are valid!

✨  Done in 0.86s.
```
We've received [feedback](https://bsky.app/profile/danabra.mov/post/3lr46ciujjs2r) that the compiler docs are difficult to understand and not prominent enough that people don't realize the compiler is a serious project and is near stable.

This PR rewrites the whole compiler doc section, giving it its own category as well as a standalone reference page.

Preview: https://react-dev-git-pr7868-fbopensource.vercel.app/
@poteto
Copy link
Member Author

poteto commented Jul 21, 2025

👋 Have a question around the removal of useCallback declaration

  1. Could you confirm how the arrow function will behave after removing the useCallback?

For performance improvement, it's recommended to use the jsx-no-bind to memoize our function and prevent creating a new instance in every re-render

Do we have an equivalent of the rule for the React-compiler, or will it be onPress={() => { some actions}} automatically memoized?

https://playground.react.dev/ would be the best way to see for yourself. React Compiler does automatically memoize callbacks as well, including lambdas in JSX.

Copy link
Member

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Phenomenal job. Let's land and iterate on any feedback.

@poteto poteto merged commit b165e71 into main Jul 21, 2025
11 checks passed
@poteto poteto deleted the pr7868 branch July 21, 2025 16:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.