React 19 and React Compiler
React 19 and React Compiler
React 19 marks one of React's larger inflection points. Among its changes, React Compiler automates some of the memoization that used to be written by hand.
1. About React
React was created in 2011 by Jordan Walke at Facebook for an internal tool (News Feed) and was open-sourced at JSConf US in May 2013. The license is MIT (briefly BSD+Patents in 2017, then reverted to MIT).
| Version | When | Event |
|---|---|---|
| 0.14 | 2015 | react-dom split out. |
| 16 | 2017-09 | Fiber rewrite. error boundary, fragments, portals. |
| 16.8 | 2019-02 | Hooks officially. useState, useEffect. |
| 17 | 2020-10 | "no new features" release. Gradual upgrade path. |
| 18 | 2022-03 | Concurrent renderer, automatic batching, useTransition. |
| 19 | 2024-12 GA | Server Components stable, use, Actions, ref-as-prop, <form action> integration. |
Alongside 19 (as a separate package), React Compiler reached the RC stage.
2. React 19 essentials
- Actions —
<form action={fn}>oruseActionStateanduseFormStatusbundle the pending/error/optimistic state of form submission into a single API. use(thenable)— unwrap promises or context directly inside a component. Pairs with Suspense.- ref as prop —
forwardRefbecomes effectively unnecessary.function X({ ref }) { ... }is now valid. useOptimistic— a hook for optimistic UI updates.- Metadata integration — declare
<title>,<meta>,<link rel="stylesheet">anywhere in the component tree and React lifts them into the head.
3. Compiler-driven automation
In traditional React, re-render optimizations are written by hand.
const value = useMemo(() => heavy(a, b), [a, b])
const onClick = useCallback(() => setX(x + 1), [x])
const Memo = memo(Child)
Compiler analyzes component functions at build time and inserts memoization automatically so that the same input reuses the same result (the official docs call it "auto-memoization"). As a result, manual useMemo, useCallback, and memo calls become rarer. The output uses cache slots from react-compiler-runtime's c().
Preconditions apply. The code must follow React's rules.
- Render functions are pure (same props/state yield the same output).
- Do not mutate props or state during render.
- Honor the rules of Hooks (top-level calls, no conditional calls).
When rules are broken, Compiler skips that component (opt-out). The eslint-plugin-react-compiler package catches violations ahead of time.
4. Where Compiler does not help
Auto memoization handles referential equality within component boundaries. The following still require human attention.
- External mutation dependencies — patterns where
useEffectdeps include a changing object. Compiler does not wrap effect bodies. - Observer / subscription —
IntersectionObserverandResizeObservercallbacks need stable references for effect cleanup to work correctly.useCallbackoruseEffectEventmay be required. - AbortController patterns — calling abort on a controller created inside an effect must be done by hand in cleanup.
- Spread deps — dynamic-length deps like
useEffect(fn, [...deps])cannot be guaranteed by Compiler. - External library callbacks — when a library requires the same reference (for example, matching
addEventListenerandremoveEventListenerpairs), explicit preservation is safer.
In short, Compiler reduces the need to "newly write manual memoization" but does not replace every case of it.
5. UI library comparison
| Library | First release | Notes |
|---|---|---|
| React | 2013 | VDOM, Hooks, Server Components. |
| Vue | 2014, Evan You | reactive proxy, single-file components. |
| Svelte | 2016, Rich Harris | The compiler erases most of the runtime. Runes introduced in 5 (2024). |
| Solid | 2021, Ryan Carniato | fine-grained reactivity. Uses JSX. |
| Preact | 2015 | A small React-compatible implementation. About 3KB. |
| Qwik | 2022, Misko Hevery | resumability — a model that defers hydration. |
Compiler is React's attempt to reduce the burden of manual memoization that is unique to React. Other libraries have different answers in the same place (Vue's reactive, Svelte's compile-time tracking, Solid's signals).
6. Install and setup
pnpm add react@19 react-dom@19
pnpm add -D babel-plugin-react-compiler eslint-plugin-react-compiler
// babel.config.js
export default {
plugins: [
["babel-plugin-react-compiler", { /* options */ }]
]
}
Next.js wires it through the experimental.reactCompiler option in next.config.js, and Vite wires it via vite-plugin-react's babel.plugins entry.
7. Form actions in 19
import { useActionState } from "react"
async function submit(prev: State, form: FormData) {
const r = await fetch("/api", { method: "POST", body: form })
return await r.json()
}
export function NewItem() {
const [state, action, pending] = useActionState(submit, { error: null })
return (
<form action={action}>
<input name="title" />
<button disabled={pending}>저장</button>
{state.error && <p>{state.error}</p>}
</form>
)
}
8. The shape Compiler handles
// before: hand memoization
const items = useMemo(() => list.filter(p), [list, p])
// after: just an expression. Compiler stores it in a cache slot.
const items = list.filter(p)
9. Common pitfalls
Gradual adoption — Compiler can be enabled per file or per directory. In a large codebase, staged adoption is safer than enabling everything at once.
DevTools indication — components that Compiler applies to get a marker like "Memo ✨" in React DevTools. If it's missing, the component may have been skipped due to rule violations.
Strict Mode's double invocation — React 18+ Strict Mode intentionally runs effects twice. Code with side effects must be idempotent regardless of Compiler.
Auto-memoization is not free — the cache comparison itself adds cost. Reports indicate that very small components can actually become slower. The general recommendation is "enable Compiler, measure, then decide."
Coexistence with manual memoization — there is no need to forcibly remove existing useMemo and useCallback. Compiler does not stack its cache on top of them; it leaves them alone.
Closing thoughts
React Compiler significantly reduces the burden of manual memoization. It does not replace every case, though. Code that follows the rules + manual memoization preserved for exceptional cases (Observer, external callbacks, etc.) is the most balanced flow.
Next
- nextjs-app-router
- state-philosophy
We refer to the React official site, React Compiler docs, React 19 release notes, Rules of React, eslint-plugin-react-compiler, Vue.js, and SolidJS.