Migrate every React 17 app to React 19.
For each repository:
1. Update `package.json`:
- `react` and `react-dom` to `^19.0.0`
- `@types/react` and `@types/react-dom` to the matching v19 majors
- Bump compatible peers: `react-router` (6.x+), `@testing-library/react` (16.x+),
`next` if used (15.x+)
2. Update the root render call:
- Replace `ReactDOM.render(<App />, root)` with
`ReactDOM.createRoot(root).render(<App />)`
- Replace `ReactDOM.hydrate(...)` with `ReactDOM.hydrateRoot(...)`
3. Remove APIs deleted in React 18/19:
- String refs (`ref="myRef"`) → `useRef`/`createRef`
- Legacy context (`childContextTypes`, `getChildContext`) → `createContext`
- `ReactDOM.findDOMNode` → forwarded refs
- `defaultProps` on function components → ES default parameters
4. Adopt the new JSX transform, remove unused `import React from 'react'`
statements where the file does not reference `React` directly.
5. Update Suspense/concurrent-affected patterns: wrap data-loading components in
`<Suspense>` where they use `use()` or new React 19 hooks.
6. Update CI Node version to 20.x (React 19 minimum). The Problem
Skipping React 18 means absorbing two majors of breaking changes at once. React 18 removed the legacy `ReactDOM.render` API in favour of `createRoot`; React 19 removes string refs, legacy context, `defaultProps` on function components, and `findDOMNode`. The new JSX transform makes most `import React from 'react'` statements obsolete, and TypeScript types tightened in ways that surface latent bugs in component props.
The peer-dependency story is worse than the runtime story. Every UI library, every test library, every routing/data-fetching package needs to be on a React 19-compatible major. `@testing-library/react` 16, `react-router` 6+, `@types/react` 19, `next` 15, each with its own breaking changes. At organizational scale, dozens of frontends with overlapping but non-identical dependency stacks means every repo is a slightly different migration. Doing this one PR at a time across a UI org is months of coordination work.
What Tidra Does
- Bumps
react,react-dom, and matching@types/*packages to v19 in everypackage.json - Bumps peer dependencies that have React 19 requirements (
react-router,@testing-library/react,next, etc.) to compatible majors - Rewrites root rendering,
ReactDOM.render→createRoot(...).render(...),ReactDOM.hydrate→hydrateRoot - Replaces APIs removed in React 18/19, string refs, legacy context,
findDOMNode, function-componentdefaultProps - Strips unused
import React from 'react'statements where the file doesn't referenceReactdirectly, leveraging the new JSX transform - Updates CI Node version to 20.x (React 19's runtime floor) and re-runs
npm testto surface remaining breakages
Before & After
Customization Tips
- Skip via 18 or direct: Going 17 → 19 in one PR is faster but riskier. For large apps, consider a 17 → 18 stop first to isolate the
createRootswitch from the API removals. - Strict mode: React 19 strict mode double-invokes effects in development. Apps that have been quietly relying on single-invoke behaviour will need effect cleanups audited.
- Component library compatibility: Confirm your design system (MUI, Chakra, Ant Design, etc.) has a React 19-compatible release before bulk-migrating consumer apps. Pinning to RC versions causes churn.
- Test coverage gate: Require
npm testand a production build per repo before merging. Type errors from the React 19@typesupdates often only surface at build time.