Skip to content
Journal

Engineering

React 19 RC and the Compiler: What We Are Actually Doing About It

Headshot of Grace Nakamura
Grace Nakamura
May 8, 2024 · 3 min read

React 19 reached release candidate at the end of April, and alongside it the team open-sourced the React Compiler, the tool formerly teased as React Forget. For a studio that ships and maintains a lot of client React, this is the most consequential release since hooks, so here is our working read and our actual upgrade posture, written down so clients can stop asking us one at a time.

The headline is deletion

The best features in React 19 are the ones that remove code we were tired of writing. forwardRef is gone as a requirement; refs pass as normal props. The use API cleans up async data reads. Actions formalize the pending, error, and optimistic-update dance that every form on every site reimplemented slightly differently. Each of these deletes a category of boilerplate that juniors tripped on and seniors wrote on autopilot.

But the compiler is the real event. React's performance model has always had a tax hidden in it: the framework re-renders liberally, and escaping the cost meant hand-annotating your code with useMemo, useCallback, and memo. In practice this meant every serious codebase accumulated hundreds of these annotations, applied inconsistently, drifting out of date, occasionally caching bugs into place. Memoization was a performance tool that doubled as a code review argument generator.

The compiler makes React do this automatically, at build time, by analyzing your components and memoizing what can safely be memoized. Code like this:

function ProductGrid({ products, filters }) {
  const visible = applyFilters(products, filters);
  return visible.map(p => <ProductCard key={p.id} product={p} />);
}

just gets faster, with no annotations, because the compiler proves what depends on what. The mental model shift is real: performance stops being a manual discipline sprinkled through the code and becomes a property of the build. We ran the compiler on two internal codebases this week. It compiled the overwhelming majority of components untouched, flagged a handful that violated the rules of React, and every flag was a genuine latent bug. That last part deserves emphasis: the compiler is also the strictest linter React has ever had.

Our upgrade posture for client work

We are enthusiastic and we are not reckless, so the policy is graduated:

  • New internal tools and prototypes: React 19 RC now, compiler on. This is where we buy experience cheaply.
  • New client builds shipping after the stable release: architected for 19, compiler evaluated per project once it exits experimental status.
  • Existing client production apps: no rush. We wait for stable, let the ecosystem's libraries catch up, and schedule upgrades with the first meaty feature work rather than as standalone churn. Framework upgrades that ride along with funded work are upgrades that actually happen.

The RC-era caveats are real. Some popular libraries need updates for the new ref behavior, the compiler is explicitly labeled experimental for production, and suspense timing changes need testing in data-heavy apps. None of this is alarming. All of it is why "wait for stable, then move deliberately" remains the professional answer.

The bigger pattern

Step back and React 19 fits the year's theme in frontend: the platforms and frameworks are absorbing complexity that application teams used to carry. View transitions replaced router animation libraries. INP pushed frameworks to care about interaction cost. Now the compiler retires manual memoization. Our job description quietly improves with each of these, because every hour not spent on useCallback audits is an hour spent on the product.

Less code, faster defaults, stricter correctness. That is a release worth upgrading for, on a schedule worth being boring about.

Building something this could apply to?

We take on a small number of flagship projects each quarter.

Start a project