Topic 19.1
What's New in React 19
In one line
React 19 (December 2024) added Actions and the form hooks, use, document metadata, resource loading, ref as a prop and <Context> as a provider, and removed long-deprecated APIs such as ReactDOM.render, string refs, legacy context, propTypes checks and defaultProps on function components; 19.1 and 19.2 added Owner Stacks, <Activity>, useEffectEvent and more.
Think of it like this
A new model of a car. The steering wheel and pedals are where they always were, so you can drive it straight away. But there are new buttons (cruise control, a parking camera), and the old cassette player is gone. Before you drive, you check two lists: what's new, and what was removed so your old cassettes don't surprise you. React 19 is that new model: your components, props, state and hooks work the same, there are new features, and some old parts were taken out.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Major version
- A release whose first number changes, like 18 to 19. Only these may break old code.
- Action
- A function (often async) that React runs inside a transition, tracking its pending state, errors and order.
- Transition
- An update React treats as not urgent, so typing and clicking stay responsive while it runs.
- Deprecated
- Still works for now but will be removed later. Usually it prints a warning in development.
- Removed
- Gone. The old code no longer works or is silently ignored.
- Codemod
- A program that rewrites your code automatically, for example to move from an old API to a new one.
- Legacy context
- React's original context API for classes (
contextTypes,getChildContext). Replaced bycreateContextand removed in 19. - String ref
- The oldest ref style:
ref="box"withthis.refs.box. Removed in 19. - Owner Stack
- The chain of components that rendered a component. Shown in React 19.1+ error messages in development.
Step by step
01Read the version you actually run
Every package has a version in package.json, and React exports it as version. Check it before you trust a tutorial: an article written for React 17 may use APIs that no longer exist.
npm ls react react-dom
# my-app@1.0.0
# ├── react@19.2.8
# └── react-dom@19.2.802Step 1: go to 18.3 and read the warnings
React 18.3 behaves exactly like 18.2 but warns about everything 19 removes: ReactDOM.render, string refs, defaultProps on functions, legacy context and more. Fix each warning while you are still on a version where the old code works.
npm install react@18.3 react-dom@18.3
npm run dev # open the console, fix every warning03Step 2: install 19 and run the codemods
Install React 19 and its types, then let the codemods do the mechanical edits. Review the diff they make like any other code change: commit before running them so you can see exactly what changed.
npm install react@^19 react-dom@^19
npm install -D @types/react@^19 @types/react-dom@^19
npx codemod@latest react/19/migration-recipe
npx types-react-codemod@latest preset-19 ./src04Step 3: replace the removed entry points
The most common change in older apps is the mount line. ReactDOM.render(<App />, el) becomes createRoot(el).render(<App />), and unmountComponentAtNode(el) becomes root.unmount(). createRoot has existed since React 18, so this change works on 18.3 too.
// Before (React 17 style, removed in 19)
import ReactDOM from "react-dom";
ReactDOM.render(<App />, document.getElementById("root"));
// After
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(<App />);
// later: root.unmount();05Step 4: replace the silent removals
Some removals don't throw, they just stop working: defaultProps on function components and propTypes. Search for them (grep -rn "defaultProps\|propTypes" src) and replace them with default parameters and TypeScript types.
// Before: ignored in React 19
function Hello({ name }) { return <p>Hello, {name}</p>; }
Hello.defaultProps = { name: "friend" };
Hello.propTypes = { name: PropTypes.string };
// After: plain JavaScript defaults + TypeScript
function Hello({ name = "friend" }: { name?: string }) {
return <p>Hello, {name}</p>;
}06The map of this phase
The rest of the phase follows the new features in the order they build on each other: async transitions first, then forms on top of them, then the hooks that read their state, then the smaller improvements.
Try it yourself
- 1
Bring the old greeting back
In the first example, predict what the old greeting shows if you write
<OldHello name="Ravi" />. Then changeOldHelloitself so it showsfriendwithout being passed a name, using a default parameter. - 2
Find the new exports
In the second example add a line for
typeof React.useEffectEventandtypeof React.Activity. Predict the result (both are React 19.2), then run. - 3
Unmount with the new API
Add
setTimeout(() => root.unmount(), 500)at the end of the second example. The paragraph disappears:root.unmount()is the replacement forunmountComponentAtNode.
Code & diagrams
`defaultProps` was ignored, so `name` is `undefined`, and the broken `propTypes` validator never ran: no warning at all. Classes still support `static defaultProps`.
Preview
Console
Nothing logged yet.Expected console output
Old: Hello, undefined
New: Hello, friendThe old mount and lookup functions are simply not there any more. Everything new in this phase is a named export from `react` or `react-dom`.
Preview
Console
Nothing logged yet.Expected console output
React 19.2.8
render: undefined
hydrate: undefined
unmountComponentAtNode: undefined
findDOMNode: undefined
act in react: function
useActionState: function
useOptimistic: function
use: function
useFormStatus: function
requestFormReset: function
preload: function# 1. Upgrade to 18.3 first and fix every warning in the console
npm install react@18.3 react-dom@18.3
# 2. Install React 19 and its types
npm install react@^19 react-dom@^19
npm install -D @types/react@^19 @types/react-dom@^19
# 3. Rewrite removed JavaScript APIs (ReactDOM.render, string refs,
# act imports, useFormState, propTypes...). It asks which folder to change.
npx codemod@latest react/19/migration-recipe
# 4. Fix TypeScript type changes (useRef argument, ref callbacks, JSX namespace)
npx types-react-codemod@latest preset-19 ./src
# 5. Review what changed, then test
git diff --stat
npm testBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use a string ref in a class component
In a class component, render <div ref="box"> and read this.refs.box in componentDidMount.
Break #2
Rely on defaultProps after upgrading
Keep Hello.defaultProps = { name: "friend" } on a function component and render <Hello />.
Myth vs fact
Myth
React 19 is a rewrite and my components must change.
Fact
Function components, props, state, effects and hooks work as before. Most apps change only the mount line, a few legacy patterns, and types.
Myth
forwardRef was removed in React 19.
Fact
It still works. It is simply no longer needed for function components, because ref is a normal prop. React plans to deprecate it later.
Myth
Actions require a server framework.
Fact
Actions are plain client functions passed to transitions or forms. Server Functions (Phase 20) are one kind of function you *can* pass, not a requirement.
Myth
Removing propTypes makes React less safe.
Fact
propTypes only warned at runtime in development. TypeScript (Phase 12) catches the same mistakes before the code runs, in every environment.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
React 18.3 exists only as an upgrade aid: it is 18.2 plus deprecation warnings for each 19 removal. Upgrading 18.2 → 18.3 → 19 turns silent breakages (like ignored
defaultProps) into visible warnings. - ▸
In 19, errors are no longer re-thrown after a boundary catches them, which removes duplicate logs. Error reporting services hook in with
createRoot(el, { onCaughtError, onUncaughtError, onRecoverableError })instead of patchingconsole.error(Topic 19.8). - ▸
useFormStatefromreact-domwas the canary name ofuseActionState.react-dom19 still exports it, but it logsReactDOM.useFormState has been renamed to React.useActionState.Code written against canaries or early Next.js 14 docs needs the rename (Topic 19.4). - ▸
The TypeScript types changed with 19:
useRefneeds an argument, ref callbacks must not implicitly return a value (cleanup functions are now meaningful), the globalJSXnamespace moved toReact.JSX, andReactElement['props']defaults tounknown.types-react-codemodfixes most of these.
Remember this
- 1
A major version is a release whose first number changes (18 → 19). By the rules of semantic versioning (the numbering scheme most npm packages follow), only a major version may break code that worked before. React 19.0 shipped on 5 December 2024. Before it, React 18.3 was released: it is 18.2 plus warnings for everything 19 removes, so the safe path is to upgrade to 18.3 first, fix every warning, then move to 19.
- 2
The big new idea: Actions. An Action is a function, often
async, that React runs inside a transition (a non-urgent update, Topic 16.7). React tracks it, gives you a pending flag while it runs, sends thrown errors to the nearest error boundary, and runs updates in order. On top of that sit new APIs:<form action={fn}>andformActionon buttons (Topic 19.3),useActionState(19.4),useFormStatusfromreact-dom(19.5),useOptimistic(19.6) andrequestFormReset(19.3). - 3
Other new features:
use()reads a promise or a context (Topic 14.4). Components can render<title>,<meta>and<link>and React moves them into<head>(document metadata, Topic 19.7). Stylesheets get aprecedenceprop, async scripts are de-duplicated, andreact-domexportspreload,preinit,preconnectandprefetchDNS(19.7). Function components accept **refas a normal prop**, soforwardRefis no longer needed; ref callbacks can return a cleanup function; and<ThemeContext value={...}>works as a provider without.Provider(Topic 19.8). Server Components and Server Functions became stable for frameworks (Phase 20). - 4
Removed in React 19 (each one warned in 18.3):
ReactDOM.render,ReactDOM.hydrate,unmountComponentAtNodeandfindDOMNodeare gone fromreact-dom(usecreateRoot,hydrateRoot,root.unmount()and refs). String refs (ref="box"withthis.refs.box) now throw. Legacy context (contextTypes,childContextTypes,getChildContextin class components) was removed: React logs that it was removed and the context is not passed. **propTypeschecks** were removed: React no longer readspropTypes, so wrong prop types produce no warning (use TypeScript, Phase 12). **defaultPropson function components** is ignored: use JavaScript default parameters (function Hello({ name = "friend" })). Classes keepstatic defaultProps. Also removed:createFactory, module pattern factories, the UMD builds (load React from an ESM CDN instead) and the old JSX transform (React 19 requires the new JSX transform from 2020, which every current tool already uses). - 5
Deprecated or moved:
actmoved fromreact-dom/test-utilstoreact(import { act } from "react"); the old import still works but logs a deprecation warning, and the rest ofreact-dom/test-utilswas removed.react-test-rendereris deprecated: React recommends Testing Library (Phase 17). Readingelement.refon a JSX element now warns (refis a regular prop).forwardRefstill works and will be deprecated in a future version. Errors changed too: in 19, an error caught by a boundary is reported once withconsole.errorinstead of being re-thrown, andcreateRootacceptsonUncaughtErrorandonCaughtError(Topic 19.8). - 6
Upgrading a real app: (1) move to React 18.3 and fix its warnings, (2) install
react@^19andreact-dom@^19(and@types/react@^19,@types/react-dom@^19for TypeScript), (3) run the official codemods (programs that rewrite your source code automatically):npx codemod@latest react/19/migration-recipefor JavaScript changes (string refs,ReactDOM.render,act,propTypes…) andnpx types-react-codemod@latest preset-19 ./srcfor type changes (for exampleuseRef()now needs an argument, andReactElementprops default tounknown). (4) Run your tests and click through the app with the console open. - 7
React 19.1 (March 2025) added Owner Stacks:
captureOwnerStack()fromreactreturns, in development only, the chain of components that *rendered* the current one, which makes error messages point to the right component. React 19.2 (October 2025) added<Activity>to hide parts of the UI while keeping their state (Topic 16.10),useEffectEvent(Topic 7.9),cacheSignalfor Server Components, Performance tracks in Chrome DevTools, and partial pre-rendering on the server. All of it is additive: code written for 19.0 runs unchanged.
Explain it without notes
What are the headline features of React 19?
Which APIs were removed in React 19, and what replaces each?
How would you upgrade a large React 18 app to React 19 safely?
Why can removing defaultProps support cause bugs that tests miss?
Practice
Rewrite this mount code for React 19: ReactDOM.render(<App />, document.getElementById("root")); and later ReactDOM.unmountComponentAtNode(document.getElementById("root"));.
Rewrite function Badge({ color, label }) { … } with Badge.defaultProps = { color: "gray", label: "New" } for React 19.
Trade-offs
- ↔
Upgrading early gives you Actions and the new hooks, but old third-party libraries that use removed APIs (string refs,
findDOMNode, legacy context) may break and need updates or replacements. - ↔
Codemods save hours on large codebases but make mechanical edits; review the diff, because a codemod can't know which
defaultPropswere relied on.
Done when you can
I can list the main new features of React 19.
I can name the removed APIs and their replacements.
I can describe a safe upgrade path through React 18.3.
I can replace
defaultPropswith default parameters and string refs withuseRef.I know what 19.1 and 19.2 added.