facebook / react · Issue No. 37540
React Compiler (babel-plugin-react-compiler)
https://gist.github.com/alejo-hernandez-krevyos/86b0da81923321863e810e6e918da5e1 — repro-input.tsx is the 12-line component; the three repro-output-* files are the compiler's output from babel-plugin-react-compiler 1.0.0, 0.0.0-experimental-a1856f3-20260507, and the Rust port via oxc-transform-react 0.149.0 (identical result in all three).
Compile this component (any of the three versions above, default options, target 19):
import { useLocation } from "./router"
import { TABLE, detect, routesFor, nameOf } from "./registry"
export function Sidebar({ navigation }: { navigation?: string[] }) {
const pathname = useLocation()
const key = detect(pathname)
const active = key ? TABLE[key] : null
const contextNavigation = active ? routesFor(active.slug) : []
const effective = navigation ?? contextNavigation
const title = key ? nameOf(key) : "home"
// unused, kept for a later design change
const Icon = active?.icon
return <aside title={title}>{effective.length}</aside>
}
The compiler moves const active = … into a memo block (if ($[0] !== navigation || $[1] !== pathname) { … }), prunes the unused Icon binding, but keeps its initializer as a bare expression statement outside that block, presumably because a property read may have a getter side effect:
const title = t1;
active?.icon; // <- `active` is not declared in this scope
let t2;
At runtime this is ReferenceError: active is not defined (Safari: Can't find variable: active) on the first render of the component. Minifiers keep the name because it is a free identifier, so the production bundle carries it verbatim.
Expected: the unused declaration is dropped entirely (its initializer is a pure property read on a local), or the statement stays inside the scope that declares active, or the compiler bails out on the function.
Actual: a reference to a variable that no scope declares. The source passes tsc, ESLint (eslint-plugin-react-hooks 7.1), and unit tests on uncompiled output, so nothing that reads the source can see it — we found it in production, where every page rendering this component crashed.
Removing the unused line, or reading active?.icon inside JSX, makes the output correct. Nearby open reports (moved references, different shapes): #34901, #35342, #35386, #36128.
Every time
19.2.8
1.0.0 (also reproduced on 0.0.0-experimental-a1856f3-20260507 and on the Rust port via oxc-transform-react 0.149.0)
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.