Upgrade React 18 to 19: a practical checklist
Updated 2026-11-03
React 19 removes APIs that have been deprecated for years, and the types require an argument to useRef. Install React 18.3 first so those warnings appear while the app still runs, then install 19, run the official codemods, and build. A peer conflict with an old library is a separate decision, not a reason to pass --legacy-peer-deps and hope.
The checklist follows the React 19 upgrade guide and the React 19 release post. It is for an app you can run: a Vite project, a Lovable or Bolt export, or a Next.js app whose installed version already accepts React 19. If you are also moving Next.js 15 to 16, do that on the Next.js checklist. Its manual install already selects React 19. Do not do both bumps by asking a chat to “upgrade React” with no build afterwards.
Install React 18.3 and read the warnings
The upgrade guide published react@18.3 as the same code as 18.2 plus warnings for deprecated APIs and other changes React 19 needs. Install that release first, on a committed tree, so the warnings are visible before the APIs disappear.
git status
npm install react@18.3 react-dom@18.3
npm run devUse the dev script the project already has. Read the console. Warnings about ReactDOM.render, string refs, findDOMNode, and legacy context are the list you will fix in the steps below. If the app does not start on 18.3, fix that before you touch 19. You are still on the same major.
Install React 19 and the matching types
The upgrade guide’s install is exact about the packages and the save flag. React and React DOM move together. If the project uses TypeScript, the type packages move in the same change.
npm install --save-exact react@^19.0.0 react-dom@^19.0.0
npm install --save-exact @types/react@^19.0.0 @types/react-dom@^19.0.0Skip the second command when there is no TypeScript. Yarn’s form in the same guide is yarn add --exact with the same specifiers. A pnpm project should use pnpm rather than writing an npm lockfile beside pnpm-lock.yaml. Before you run this on a Next.js app, check the installed Next version:
npm ls next react
npm view next@YOUR_VERSION peerDependencies.reactSubstitute the version npm ls printed. Next.js 15.0.0 accepted React 18.2 or a React 19 release candidate. Later Next.js 15 releases, and Next.js 16 releases checked on 3 November 2026, also accept ^19.0.0 while still allowing React 18.2. If the range you printed does not include ^19.0.0, stay on 18 or upgrade Next first. --legacy-peer-deps is how you install a React that Next has not accepted. That is the failure mode in fixing ERESOLVE, not a step on this page.
React 19 also requires the JSX transform that has shipped with standard tooling since 2020. If the console says the app or a dependency is using an outdated JSX transform, follow the transform announcement. A current Vite or Next app already uses it.
Run the official codemods
The upgrade guide’s recipe runs the common replacements. It does not apply the TypeScript changes. Run the type preset afterwards if @types/react is installed. Pass the directory that contains the source; . is that directory when your shell is already there.
npx codemod@latest react/19/migration-recipe
npx types-react-codemod@latest preset-19 .
git diffThe recipe includes replacing ReactDOM.render, string refs, and act imports, plus a propTypes-to-TypeScript helper. Read the diff. A codemod comment left in a file is an unfinished step. Commit the result before you hand-edit, so a bad follow-up is easy to undo.
Replace the APIs React 19 removes
Search for what the codemod did not touch. These are the removals that show up in older generated apps and in hand-written entry files:
| What 18 still ran | What 19 requires |
|---|---|
ReactDOM.render | createRoot from react-dom/client, then root.render |
ReactDOM.hydrate | hydrateRoot from react-dom/client |
unmountComponentAtNode | root.unmount() |
findDOMNode and string refs such as ref="input" | A ref object or a ref callback. String refs are removed. |
defaultProps on a function, and propTypes checks | Default parameters. PropTypes checks from the React package are ignored. Class components still support defaultProps. |
act from react-dom/test-utils | Import act from react. The other test-utils functions error when called. |
import { createRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('root');
if (!container) throw new Error('Missing root element');
const root = createRoot(container);
root.render(<App />);With the React 19 types, useRef() with no argument is a type error. Pass null or undefined. A ref callback that returns a value is also rejected, because React 19 treats a returned function as a cleanup. Use a block body when you only meant to assign the node. forwardRef still works. You can also take ref as a normal prop on a function component; that is new, not a removal.
Errors thrown during render are no longer rethrown. Uncaught errors go to window.reportError, and errors an error boundary catches go to console.error. If your reporting depended on the rethrow, pass onUncaughtError and onCaughtError to createRoot, as the upgrade guide shows.
Build, test, and fix peer conflicts on purpose
npm run build
npm testWalk the sign-in screen, any form, and a route that opens a dialog. Those are where an old ref or an old render call hides. If npm install stopped with ERESOLVE because a UI kit still peers React 18, read that kit’s changelog. Upgrade the kit when it supports 19. Stay on React 18 when it does not. Do not pass --legacy-peer-deps so the kit installs and then calls ReactDOM.render at runtime. UMD builds are gone in 19. A page that loaded React from a unpkg UMD URL needs an ESM CDN such as the one the upgrade guide links, or a bundler.
New APIs such as useActionState and ref as a prop can wait. The upgrade is done when the build passes on the APIs you already use. Features are listed in the React 19 release post, after this checklist.
For weekly upkeep, you can manually create a recurring dependency-review task with Denly’s task schedules after connecting a project and runner. Configure its instructions and review gates before enabling it. The public report form below creates a report; it does not create a schedule or a pull request.
Commands and product behavior on this page were checked against the vendor documentation linked here on 3 November 2026. Those products change their screens. If a label has moved, follow the linked page rather than a memory of this one.
Read next
Upgrade Next.js 15 to 16: a practical checklist · Fix npm ERESOLVE and peer dependency conflicts
Denly