---
title: React 19 API Migrations Reference
---
# React 19 API Migrations Reference
Complete before/after patterns for all React 19 breaking changes and removed APIs.
---
## ReactDOM Root API Migration
React 19 requires `createRoot()` or `hydrateRoot()` for all apps. If the React 18 migration already ran, this is done. Verify it's correct.
### Pattern 1: createRoot() CSR App
```jsx
// Before (React 18 or earlier):
import ReactDOM from 'react-dom';
ReactDOM.render(, document.getElementById('root'));
// After (React 19):
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render();
```
### Pattern 2: hydrateRoot() SSR/Static App
```jsx
// Before (React 18 server-rendered app):
import ReactDOM from 'react-dom';
ReactDOM.hydrate(, document.getElementById('root'));
// After (React 19):
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), );
```
### Pattern 3: unmountComponentAtNode() Removed
```jsx
// Before (React 18):
import ReactDOM from 'react-dom';
ReactDOM.unmountComponentAtNode(container);
// After (React 19):
const root = createRoot(container); // Save the root reference
// later:
root.unmount();
```
**Caveat:** If the root reference was never saved, you must refactor to pass it around or use a global registry.
---
## findDOMNode() Removed
### Pattern 1: Direct ref
```jsx
// Before (React 18):
import { findDOMNode } from 'react-dom';
const domNode = findDOMNode(componentRef);
// After (React 19):
const domNode = componentRef.current; // refs point directly to DOM
```
### Pattern 2: Class Component ref
```jsx
// Before (React 18):
import { findDOMNode } from 'react-dom';
class MyComponent extends React.Component {
render() {
return
this.node = ref}>Content
;
}
getWidth() {
return findDOMNode(this).offsetWidth;
}
}
// After (React 19):
// Note: findDOMNode() is removed in React 19. Eliminate the call entirely
// and use direct refs to access DOM nodes instead.
class MyComponent extends React.Component {
nodeRef = React.createRef();
render() {
return
Content
;
}
getWidth() {
return this.nodeRef.current.offsetWidth;
}
}
```
---
## forwardRef() - Optional Modernization
### Pattern 1: Function Component Direct ref
```jsx
// Before (React 18):
import { forwardRef } from 'react';
const Input = forwardRef((props, ref) => (
));
function App() {
const inputRef = useRef(null);
return ;
}
// After (React 19):
// Simply accept ref as a regular prop:
function Input({ ref, ...props }) {
return ;
}
function App() {
const inputRef = useRef(null);
return ;
}
```
### Pattern 2: forwardRef + useImperativeHandle
```jsx
// Before (React 18):
import { forwardRef, useImperativeHandle } from 'react';
const TextInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return ;
});
function App() {
const textRef = useRef(null);
return (
<>
>
);
}
// After (React 19):
function TextInput({ ref, ...props }) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
clear: () => { inputRef.current.value = ''; }
}));
return ;
}
function App() {
const textRef = useRef(null);
return (
<>
>
);
}
```
**Note:** `useImperativeHandle` is still valid; only the `forwardRef` wrapper is removed.
---
## defaultProps Removed
### Pattern 1: Function Component with defaultProps
```jsx
// Before (React 18):
function Button({ label = 'Click', disabled = false }) {
return ;
}
// WORKS BUT is removed in React 19:
Button.defaultProps = {
label: 'Click',
disabled: false
};
// After (React 19):
// ES6 default params are now the ONLY way:
function Button({ label = 'Click', disabled = false }) {
return ;
}
// Remove all defaultProps assignments
```
### Pattern 2: Class Component defaultProps
```jsx
// Before (React 18):
class Button extends React.Component {
static defaultProps = {
label: 'Click',
disabled: false
};
render() {
return ;
}
}
// After (React 19):
// Use default params in constructor or class field:
class Button extends React.Component {
constructor(props) {
super(props);
this.label = props.label || 'Click';
this.disabled = props.disabled || false;
}
render() {
return ;
}
}
// Or simplify to function component with ES6 defaults:
function Button({ label = 'Click', disabled = false }) {
return ;
}
```
### Pattern 3: defaultProps with null
```jsx
// Before (React 18):
function Component({ value }) {
// defaultProps can set null to reset a parent-passed value
return
{value}
;
}
Component.defaultProps = {
value: null
};
// After (React 19):
// Use explicit null checks or nullish coalescing:
function Component({ value = null }) {
return
{value}
;
}
// Or:
function Component({ value }) {
return
{value ?? null}
;
}
```
---
## useRef Without Initial Value
### Pattern 1: useRef()
```jsx
// Before (React 18):
const ref = useRef(); // undefined initially
// After (React 19):
// Explicitly pass null as initial value:
const ref = useRef(null);
// Then use current:
ref.current = someElement; // Set it manually later
```
### Pattern 2: useRef with DOM Elements
```jsx
// Before:
function Component() {
const inputRef = useRef();
return ;
}
// After:
function Component() {
const inputRef = useRef(null); // Explicit null
return ;
}
```
---
## Legacy Context API Removed
### Pattern 1: React.createContext vs contextTypes
```jsx
// Before (React 18 not recommended but worked):
// Using contextTypes (old PropTypes-style context):
class MyComponent extends React.Component {
static contextTypes = {
theme: PropTypes.string
};
render() {
return
;
}
function App() {
return (
);
}
```
### Pattern 2: Class Component Consuming createContext
```jsx
// Before (class component consuming old context):
class MyComponent extends React.Component {
static contextType = ThemeContext;
render() {
return
Text
;
}
}
// After (still works in React 19):
// No change needed for static contextType
// Continue using this.context
```
**Important:** If you're still using the old `contextTypes` + `getChildContext` pattern (not modern `createContext`), you **must** migrate to `createContext` the old pattern is completely removed.
---
## String Refs Removed
### Pattern 1: this.refs String Refs
```jsx
// Before (React 18):
class Component extends React.Component {
render() {
return (
<>
>
);
}
}
// After (React 19):
class Component extends React.Component {
inputRef = React.createRef();
render() {
return (
<>
>
);
}
}
```
### Pattern 2: Callback Refs (Recommended)
```jsx
// Before (React 18):
class Component extends React.Component {
render() {
return (
<>
>
);
}
}
// After (React 19 callback is more flexible):
class Component extends React.Component {
constructor(props) {
super(props);
this.inputRef = null;
}
render() {
return (
<>
{ this.inputRef = el; }} />
>
);
}
}
```
---
## Unused React Import Removal
### Pattern 1: React Import After JSX Transform
```jsx
// Before (React 18):
import React from 'react'; // Needed for JSX transform
function Component() {
return
Text
;
}
// After (React 19 with new JSX transform):
// Remove the React import if it's not used:
function Component() {
return
Text
;
}
// BUT keep it if you use React.* APIs:
import React from 'react';
function Component() {
return
{React.useState ? 'yes' : 'no'}
;
}
```
### Scan for Unused React Imports
```bash
# Find imports that can be removed:
grep -rn "^import React from 'react';" src/ --include="*.js" --include="*.jsx"
# Then check if the file uses React.*, useContext, etc.
```
---
## Complete Migration Checklist
```bash
# 1. Find all ReactDOM.render calls:
grep -rn "ReactDOM.render" src/ --include="*.js" --include="*.jsx"
# Should be converted to createRoot
# 2. Find all ReactDOM.hydrate calls:
grep -rn "ReactDOM.hydrate" src/ --include="*.js" --include="*.jsx"
# Should be converted to hydrateRoot
# 3. Find all forwardRef usages:
grep -rn "forwardRef" src/ --include="*.js" --include="*.jsx"
# Check each one to see if it can be removed (most can)
# 4. Find all .defaultProps assignments:
grep -rn "\.defaultProps\s*=" src/ --include="*.js" --include="*.jsx"
# Replace with ES6 default params
# 5. Find all useRef() without initial value:
grep -rn "useRef()" src/ --include="*.js" --include="*.jsx"
# Add null: useRef(null)
# 6. Find old context (contextTypes):
grep -rn "contextTypes\|childContextTypes\|getChildContext" src/ --include="*.js" --include="*.jsx"
# Migrate to createContext
# 7. Find string refs (ref="name"):
grep -rn 'ref="' src/ --include="*.js" --include="*.jsx"
# Migrate to createRef or callback ref
# 8. Find unused React imports:
grep -rn "^import React from 'react';" src/ --include="*.js" --include="*.jsx"
# Check if React is used in the file
```